diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 8d41b62..e0ffe8d 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -1,54 +1,47 @@ ---- +--- description: "File a bug report" -labels: +labels: - bug-candidate name: "Bug Report" title: "[Bug-Candidate]: " -body: - - - attributes: +body: + - attributes: value: | - Before submitting, please check the issues tab to avoid duplicates. - Thanks for taking the time to fill out this bug report! + Before submitting, please check the issues tab to avoid duplicates. + Thanks for taking the time to fill out this bug report! type: markdown - - - attributes: + - attributes: label: "Describe the issue:" id: what-happened type: textarea - validations: + validations: required: true - - - attributes: + - attributes: label: "Steps to reproduce the issue:" description: "Please be as detailed as you can. It's easier for us to fix the bug if we can reproduce it." placeholder: "First, clone the `crytic/properties` repository..." id: reproduce type: textarea - validations: + validations: required: true - - - attributes: + - attributes: label: "If additional code is needed for reproducing, please copy it here, or drop us a link to the repository:" description: "It can be a github repo, code snippet, or empty if no additional code is needed." placeholder: "`contract A {}`\n" id: additional-code type: textarea - - - attributes: + - attributes: label: "Echidna version:" description: "Run `echidna-test --version`" id: version type: textarea - validations: + validations: required: true - - - attributes: + - attributes: label: "Additional information:" description: | - Please add any additional information you might consider important, or copy and paste any relevant log output. - This will be automatically formatted into code, so no need for backticks or other formatting. - render: shell + Please add any additional information you might consider important, or copy and paste any relevant log output. + This will be automatically formatted into code, so no need for backticks or other formatting. + render: shell id: additional-information type: textarea - diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml index fe04776..fabecd2 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yaml +++ b/.github/ISSUE_TEMPLATE/feature_request.yaml @@ -1,7 +1,7 @@ --- name: Feature request description: Suggest new features or improvements to existing features -labels: +labels: - "feature-request" title: "[Feature-request]: " body: @@ -15,4 +15,4 @@ body: label: Describe the desired feature or improvement description: Please be as specific as you can, explain why is that needed or what problem it solves. validations: - required: true \ No newline at end of file + required: true diff --git a/.github/workflows/examples.yaml b/.github/workflows/examples.yaml index a579d4d..4dba0e5 100644 --- a/.github/workflows/examples.yaml +++ b/.github/workflows/examples.yaml @@ -3,10 +3,10 @@ name: Test examples on: push: branches: - - main + - main pull_request: branches: - - '*' + - "*" env: FOUNDRY_PROFILE: ci @@ -16,100 +16,100 @@ jobs: name: Test Foundry examples runs-on: ubuntu-latest steps: - - name: Checkout repository - uses: actions/checkout@v3 - with: - submodules: recursive - - - name: Install Foundry - uses: foundry-rs/foundry-toolchain@v1 - with: - version: nightly - - - name: Compile ERC20 Foundry example - working-directory: tests/ERC20/foundry - run: forge build --build-info - - - name: Run Echidna against ERC20 Internal Foundry example - uses: crytic/echidna-action@v2 - with: - echidna-workdir: ./tests/ERC20/foundry - files: . - config: ./echidna-config.yaml - crytic-args: --ignore-compile - contract: CryticERC20InternalHarness - - - name: Run Echidna against ERC20 External Foundry example - uses: crytic/echidna-action@v2 - with: - echidna-workdir: ./tests/ERC20/foundry - files: . - config: ./echidna-config-ext.yaml - crytic-args: --ignore-compile - contract: CryticERC20ExternalHarness - - - name: Compile ERC4646 Foundry example - working-directory: tests/ERC4626/foundry - run: forge build --build-info - - - name: Run Echidna against ERC4626 Foundry example - uses: crytic/echidna-action@v2 - with: - echidna-workdir: ./tests/ERC4626/foundry - files: . - config: ./echidna.yaml - crytic-args: --ignore-compile - contract: CryticERC4626InternalHarness + - name: Checkout repository + uses: actions/checkout@v3 + with: + submodules: recursive + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly + + - name: Compile ERC20 Foundry example + working-directory: tests/ERC20/foundry + run: forge build --build-info + + - name: Run Echidna against ERC20 Internal Foundry example + uses: crytic/echidna-action@v2 + with: + echidna-workdir: ./tests/ERC20/foundry + files: . + config: ./echidna-config.yaml + crytic-args: --ignore-compile + contract: CryticERC20InternalHarness + + - name: Run Echidna against ERC20 External Foundry example + uses: crytic/echidna-action@v2 + with: + echidna-workdir: ./tests/ERC20/foundry + files: . + config: ./echidna-config-ext.yaml + crytic-args: --ignore-compile + contract: CryticERC20ExternalHarness + + - name: Compile ERC4646 Foundry example + working-directory: tests/ERC4626/foundry + run: forge build --build-info + + - name: Run Echidna against ERC4626 Foundry example + uses: crytic/echidna-action@v2 + with: + echidna-workdir: ./tests/ERC4626/foundry + files: . + config: ./echidna.yaml + crytic-args: --ignore-compile + contract: CryticERC4626InternalHarness hardhat: name: Test Hardhat examples runs-on: ubuntu-latest steps: - - name: Checkout repository - uses: actions/checkout@v3 - with: - submodules: recursive - - - name: Set up Nodejs - uses: actions/setup-node@v3 - with: - node-version: 16 - - - name: Install dependencies and compile ERC20 example - working-directory: tests/ERC20/hardhat - run: | - npm ci - npx hardhat compile --force - - - name: Run Echidna for Internal tests - uses: crytic/echidna-action@v2 - with: - echidna-workdir: ./tests/ERC20/hardhat - files: . - config: ./tests/echidna-config.yaml - crytic-args: --ignore-compile - contract: CryticERC20InternalHarness - - - name: Run Echidna for External tests - uses: crytic/echidna-action@v2 - with: - echidna-workdir: ./tests/ERC20/hardhat - files: . - config: ./tests/echidna-config-ext.yaml - crytic-args: --ignore-compile - contract: CryticERC20ExternalHarness - - - name: Install dependencies and compile ERC4626 example - working-directory: tests/ERC4626/hardhat - run: | - npm ci - npx hardhat compile --force - - - name: Run Echidna - uses: crytic/echidna-action@v2 - with: - echidna-workdir: ./tests/ERC4626/hardhat - files: . - config: tests/echidna-config.yaml - crytic-args: --ignore-compile - contract: CryticERC4626Harness + - name: Checkout repository + uses: actions/checkout@v3 + with: + submodules: recursive + + - name: Set up Nodejs + uses: actions/setup-node@v3 + with: + node-version: 16 + + - name: Install dependencies and compile ERC20 example + working-directory: tests/ERC20/hardhat + run: | + npm ci + npx hardhat compile --force + + - name: Run Echidna for Internal tests + uses: crytic/echidna-action@v2 + with: + echidna-workdir: ./tests/ERC20/hardhat + files: . + config: ./tests/echidna-config.yaml + crytic-args: --ignore-compile + contract: CryticERC20InternalHarness + + - name: Run Echidna for External tests + uses: crytic/echidna-action@v2 + with: + echidna-workdir: ./tests/ERC20/hardhat + files: . + config: ./tests/echidna-config-ext.yaml + crytic-args: --ignore-compile + contract: CryticERC20ExternalHarness + + - name: Install dependencies and compile ERC4626 example + working-directory: tests/ERC4626/hardhat + run: | + npm ci + npx hardhat compile --force + + - name: Run Echidna + uses: crytic/echidna-action@v2 + with: + echidna-workdir: ./tests/ERC4626/hardhat + files: . + config: tests/echidna-config.yaml + crytic-args: --ignore-compile + contract: CryticERC4626Harness diff --git a/.solhint.json b/.solhint.json index dcb7836..f95f06a 100644 --- a/.solhint.json +++ b/.solhint.json @@ -3,7 +3,7 @@ "rules": { "compiler-version": ["off"], "func-name-mixedcase": ["off"], - "func-visibility": ["warn",{ "ignoreConstructors":true }], + "func-visibility": ["warn", { "ignoreConstructors": true }], "no-empty-blocks": ["off"], "var-name-mixedcase": ["off"] } diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 58a7c33..b01b25a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -28,6 +28,7 @@ Some pull request guidelines: ## Directory Structure Below is a rough outline of the directory structure: + ```text . ├── contracts # Parent folder for contracts @@ -48,7 +49,7 @@ Below is a rough outline of the directory structure: │ ├── Math # Properties for mathematical libraries │ │ └── ABDKMath64x64 │ ├── util # Helpers for new or existing properties -│ └── ... +│ └── ... ├── lib # External libraries needed for the repository └── tests # Tests for properties ├── ERC20 @@ -64,6 +65,26 @@ Below is a rough outline of the directory structure: Please follow this structure in your collaborations. +## Linting and formatting + +To install the formatters and linters, run: + +```bash +npm install +``` + +The formatter is run with: + +```bash +npm run format +``` + +The linter is run with: + +```bash +npm run lint +``` + ## Running tests on your computer Please read [README.md](README.md) for instructions on how to set up your environment and run the tests. diff --git a/PROPERTIES.md b/PROPERTIES.md index 9575ec0..9a70197 100644 --- a/PROPERTIES.md +++ b/PROPERTIES.md @@ -19,203 +19,203 @@ This file lists all the currently implemented Echidna property tests for ERC20, ### Basic properties for standard functions -| ID | Name | Invariant tested | -|---|---|---| -| ERC20-BASE-001 | [test_ERC20_constantSupply](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20BasicProperties.sol#L16) | Total supply should be constant for non-mintable and non-burnable tokens. | -| ERC20-BASE-002 | [test_ERC20_userBalanceNotHigherThanSupply](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20BasicProperties.sol#L22) | No user balance should be greater than the token's total supply. | -| ERC20-BASE-003 | [test_ERC20_usersBalancesNotHigherThanSupply](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20BasicProperties.sol#L27) | The sum of users balances should not be greater than the token's total supply. | -| ERC20-BASE-004 | [test_ERC20_zeroAddressBalance](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20BasicProperties.sol#L33) | Token balance for address zero should be zero. | -| ERC20-BASE-005 | [test_ERC20_transferToZeroAddress](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20BasicProperties.sol#L38) | `transfer`s to zero address should not be allowed. | -| ERC20-BASE-006 | [test_ERC20_transferFromToZeroAddress](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20BasicProperties.sol#L47) | `transferFrom`s to zero address should not be allowed. | -| ERC20-BASE-007 | [test_ERC20_selfTransfer](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20BasicProperties.sol#L70) | Self `transfer`s should not break accounting. | -| ERC20-BASE-008 | [test_ERC20_selfTransferFrom](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20BasicProperties.sol#L58) | Self `transferFrom`s should not break accounting. | -| ERC20-BASE-009 | [test_ERC20_transferMoreThanBalance](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20BasicProperties.sol#L93) | `transfer`s for more than account balance should not be allowed. | -| ERC20-BASE-010 | [test_ERC20_transferFromMoreThanBalance](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20BasicProperties.sol#L80) | `transferFrom`s for more than account balance should not be allowed. | -| ERC20-BASE-011 | [test_ERC20_transferZeroAmount](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20BasicProperties.sol#L105) | `transfer`s for zero amount should not break accounting. | -| ERC20-BASE-012 | [test_ERC20_transferFromZeroAmount](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20BasicProperties.sol#L117) | `transferFrom`s for zero amount should not break accounting. | -| ERC20-BASE-013 | [test_ERC20_transfer](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20BasicProperties.sol#L130) | Valid `transfer`s should update accounting correctly. | -| ERC20-BASE-014 | [test_ERC20_transferFrom](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20BasicProperties.sol#L144) | Valid `transferFrom`s should update accounting correctly. | -| ERC20-BASE-015 | [test_ERC20_setAllowance](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20BasicProperties.sol#L160) | Allowances should be set correctly when `approve` is called. | -| ERC20-BASE-016 | [test_ERC20_setAllowanceTwice](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20BasicProperties.sol#L167) | Allowances should be updated correctly when `approve` is called twice. | -| ERC20-BASE-017 | [test_ERC20_spendAllowanceAfterTransfer](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20BasicProperties.sol#L178) | After `transferFrom`, allowances should be updated correctly. | +| ID | Name | Invariant tested | +| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | +| ERC20-BASE-001 | [test_ERC20_constantSupply](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20BasicProperties.sol#L16) | Total supply should be constant for non-mintable and non-burnable tokens. | +| ERC20-BASE-002 | [test_ERC20_userBalanceNotHigherThanSupply](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20BasicProperties.sol#L22) | No user balance should be greater than the token's total supply. | +| ERC20-BASE-003 | [test_ERC20_usersBalancesNotHigherThanSupply](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20BasicProperties.sol#L27) | The sum of users balances should not be greater than the token's total supply. | +| ERC20-BASE-004 | [test_ERC20_zeroAddressBalance](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20BasicProperties.sol#L33) | Token balance for address zero should be zero. | +| ERC20-BASE-005 | [test_ERC20_transferToZeroAddress](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20BasicProperties.sol#L38) | `transfer`s to zero address should not be allowed. | +| ERC20-BASE-006 | [test_ERC20_transferFromToZeroAddress](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20BasicProperties.sol#L47) | `transferFrom`s to zero address should not be allowed. | +| ERC20-BASE-007 | [test_ERC20_selfTransfer](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20BasicProperties.sol#L70) | Self `transfer`s should not break accounting. | +| ERC20-BASE-008 | [test_ERC20_selfTransferFrom](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20BasicProperties.sol#L58) | Self `transferFrom`s should not break accounting. | +| ERC20-BASE-009 | [test_ERC20_transferMoreThanBalance](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20BasicProperties.sol#L93) | `transfer`s for more than account balance should not be allowed. | +| ERC20-BASE-010 | [test_ERC20_transferFromMoreThanBalance](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20BasicProperties.sol#L80) | `transferFrom`s for more than account balance should not be allowed. | +| ERC20-BASE-011 | [test_ERC20_transferZeroAmount](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20BasicProperties.sol#L105) | `transfer`s for zero amount should not break accounting. | +| ERC20-BASE-012 | [test_ERC20_transferFromZeroAmount](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20BasicProperties.sol#L117) | `transferFrom`s for zero amount should not break accounting. | +| ERC20-BASE-013 | [test_ERC20_transfer](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20BasicProperties.sol#L130) | Valid `transfer`s should update accounting correctly. | +| ERC20-BASE-014 | [test_ERC20_transferFrom](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20BasicProperties.sol#L144) | Valid `transferFrom`s should update accounting correctly. | +| ERC20-BASE-015 | [test_ERC20_setAllowance](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20BasicProperties.sol#L160) | Allowances should be set correctly when `approve` is called. | +| ERC20-BASE-016 | [test_ERC20_setAllowanceTwice](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20BasicProperties.sol#L167) | Allowances should be updated correctly when `approve` is called twice. | +| ERC20-BASE-017 | [test_ERC20_spendAllowanceAfterTransfer](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20BasicProperties.sol#L178) | After `transferFrom`, allowances should be updated correctly. | ### Tests for burnable tokens -| ID | Name | Invariant tested | -|---|---|---| -| ERC20-BURNABLE-001 | [test_ERC20_burn](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20BurnableProperties.sol#L17) | User balance and total supply should be updated correctly when `burn` is called. | -| ERC20-BURNABLE-002 | [test_ERC20_burnFrom](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20BurnableProperties.sol#L29) | User balance and total supply should be updated correctly when `burnFrom` is called. | -| ERC20-BURNABLE-003 | [test_ERC20_burnFromUpdateAllowance](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20BurnableProperties.sol#L42) | Allowances should be updated correctly when `burnFrom` is called. | +| ID | Name | Invariant tested | +| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | +| ERC20-BURNABLE-001 | [test_ERC20_burn](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20BurnableProperties.sol#L17) | User balance and total supply should be updated correctly when `burn` is called. | +| ERC20-BURNABLE-002 | [test_ERC20_burnFrom](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20BurnableProperties.sol#L29) | User balance and total supply should be updated correctly when `burnFrom` is called. | +| ERC20-BURNABLE-003 | [test_ERC20_burnFromUpdateAllowance](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20BurnableProperties.sol#L42) | Allowances should be updated correctly when `burnFrom` is called. | ### Tests for mintable tokens -| ID | Name | Invariant tested | -|---|---|---| -| ERC20-MINTABLE-001 | [test_ERC20_mintTokens](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20MintableProperties.sol#L19) | User balance and total supply should be updated correctly after minting. | +| ID | Name | Invariant tested | +| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | +| ERC20-MINTABLE-001 | [test_ERC20_mintTokens](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20MintableProperties.sol#L19) | User balance and total supply should be updated correctly after minting. | ### Tests for pausable tokens -| ID | Name | Invariant tested | -|---|---|---| -| ERC20-PAUSABLE-001 | [test_ERC20_pausedTransfer](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20PausableProperties.sol#L35) | Token `transfer`s should not be possible when paused state is enabled. | -| ERC20-PAUSABLE-002 | [test_ERC20_pausedTransferFrom](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20PausableProperties.sol#L52) | Token `transferFrom`s should not be possible when paused state is enabled. | +| ID | Name | Invariant tested | +| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | +| ERC20-PAUSABLE-001 | [test_ERC20_pausedTransfer](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20PausableProperties.sol#L35) | Token `transfer`s should not be possible when paused state is enabled. | +| ERC20-PAUSABLE-002 | [test_ERC20_pausedTransferFrom](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20PausableProperties.sol#L52) | Token `transferFrom`s should not be possible when paused state is enabled. | ### Tests for tokens implementing `increaseAllowance` and `decreaseAllowance` -| ID | Name | Invariant tested | -|---|---|---| -| ERC20-ALLOWANCE-001 | [test_ERC20_setAndIncreaseAllowance](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20IncreaseAllowanceProperties.sol#L17) | Allowance should be updated correctly when `increaseAllowance` is called. | -| ERC20-ALLOWANCE-002 | [test_ERC20_setAndDecreaseAllowance](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20IncreaseAllowanceProperties.sol#L28) | Allowance should be updated correctly when `decreaseAllowance` is called. | +| ID | Name | Invariant tested | +| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | +| ERC20-ALLOWANCE-001 | [test_ERC20_setAndIncreaseAllowance](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20IncreaseAllowanceProperties.sol#L17) | Allowance should be updated correctly when `increaseAllowance` is called. | +| ERC20-ALLOWANCE-002 | [test_ERC20_setAndDecreaseAllowance](https://github.com/crytic/properties/blob/main/contracts/ERC20/internal/properties/ERC20IncreaseAllowanceProperties.sol#L28) | Allowance should be updated correctly when `decreaseAllowance` is called. | ## ERC4626 -| ID | Name | Invariant tested | -|---|---|---| -| ERC4626-001 | [verify_assetMustNotRevert](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/MustNotRevertProps.sol#L10) | `asset` must not revert. | -| ERC4626-002 | [verify_totalAssetsMustNotRevert](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/MustNotRevertProps.sol#L20) | `totalAssets` must not revert. | -| ERC4626-003 | [verify_convertToAssetsMustNotRevert](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/MustNotRevertProps.sol#L30) | `convertToAssets` must not revert for reasonable values. | -| ERC4626-004 | [verify_convertToSharesMustNotRevert](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/MustNotRevertProps.sol#L54) | `convertToShares` must not revert for reasonable values. | -| ERC4626-005 | [verify_maxDepositMustNotRevert](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/MustNotRevertProps.sol#L71) | `maxDeposit` must not revert. | -| ERC4626-006 | [verify_maxMintMustNotRevert](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/MustNotRevertProps.sol#L79) | `maxMint` must not revert. | -| ERC4626-007 | [verify_maxRedeemMustNotRevert](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/MustNotRevertProps.sol#L87) | `maxRedeem` must not revert. | -| ERC4626-008 | [verify_maxWithdrawMustNotRevert](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/MustNotRevertProps.sol#L99) | `maxWithdraw` must not revert. | -| ERC4626-009 | [verify_redeemViaApprovalProxy](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/RedeemUsingApprovalProps.sol#L10) | Redeem via approval proxy. | -| ERC4626-010 | [verify_withdrawViaApprovalProxy](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/RedeemUsingApprovalProps.sol#L31) | Withdraw via approval proxy. | -| ERC4626-011 | [verify_withdrawRequiresTokenApproval](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/RedeemUsingApprovalProps.sol#L56) | Withdraw requires approval for third parties. | -| ERC4626-012 | [verify_redeemRequiresTokenApproval](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/RedeemUsingApprovalProps.sol#L73) | Redeem requires approval for third parties. | -| ERC4626-013 | [verify_previewDepositRoundingDirection](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/RoundingProps.sol#L9) | checks `previewDeposit` rounding direction | -| ERC4626-014 | [verify_previewMintRoundingDirection](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/RoundingProps.sol#L15) | checks `previewMint` rounding direction | -| ERC4626-015 | [verify_convertToSharesRoundingDirection](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/RoundingProps.sol#L22) | checks `convertToShares` rounding direction | -| ERC4626-016 | [verify_previewRedeemRoundingDirection](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/RoundingProps.sol#L29) | checks `previewRedeem` rounding direction | -| ERC4626-017 | [verify_previewWithdrawRoundingDirection](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/RoundingProps.sol#L35) | checks `previewWithdraw` rounding direction | -| ERC4626-018 | [verify_convertToAssetsRoundingDirection](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/RoundingProps.sol#L42) | checks `convertToAssets` rounding direction | -| ERC4626-019 | [verify_depositRoundingDirection](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/RoundingProps.sol#L48) | checks `deposit` rounding direction | -| ERC4626-020 | [verify_mintRoundingDirection](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/RoundingProps.sol#L54) | checks `mint` rounding direction | -| ERC4626-021 | [verify_withdrawRoundingDirection](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/RoundingProps.sol#L62) | checks `withdraw` rounding direction | -| ERC4626-022 | [verify_redeemRoundingDirection](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/RoundingProps.sol#L70) | checks `redeem` rounding direction | -| ERC4626-023 | [verify_maxDepositIgnoresSenderAssets](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/SenderIndependentProps.sol#L10) | `maxDeposit` must assume the agent has infinite assets. | -| ERC4626-024 | [verify_maxMintIgnoresSenderAssets](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/SenderIndependentProps.sol#L19) | `maxMint` must assume the agent has infinite assets. | -| ERC4626-025 | [verify_previewMintIgnoresSender](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/SenderIndependentProps.sol#L28) | `previewMint` must not be dependent on `msg.sender`. | -| ERC4626-026 | [verify_previewDepositIgnoresSender](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/SenderIndependentProps.sol#L38) | `previewDeposit` must not be dependent on `msg.sender`. | -| ERC4626-027 | [verify_previewWithdrawIgnoresSender](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/SenderIndependentProps.sol#L48) | `previewWithdraw` must not be dependent on `msg.sender`. | -| ERC4626-028 | [verify_previewRedeemIgnoresSender](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/SenderIndependentProps.sol#L63) | `previewRedeem` must not be dependent on `msg.sender`. | -| ERC4626-029 | [verify_depositProperties](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/FunctionalAccountingProps.sol#L11) | `deposit` assets and shares handling. | -| ERC4626-030 | [verify_mintProperties](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/FunctionalAccountingProps.sol#L39) | `mint` assets and shares handling. | -| ERC4626-031 | [verify_redeemProperties](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/FunctionalAccountingProps.sol#L65) | `redeem` assets and shares handling. | -| ERC4626-032 | [verify_withdrawProperties](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/FunctionalAccountingProps.sol#L92) | `withdraw` assets and shares handling. | -| ERC4626-033 | [verify_sharePriceInflationAttack](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/SecurityProps.sol#L12) | Checks for share inflation attacks. | -| ERC4626-034 | [verify_assetDecimalsLessThanVault](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/SecurityProps.sol#L7) | Compares vault decimals with asset decimals. | -| ERC4626-035 | [verify_withdrawRequiresTokenApproval](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/RedeemUsingApprovalProps.sol#L56) | Verifies that `withdraw` requires token approval. | -| ERC4626-036 | [verify_redeemRequiresTokenApproval](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/RedeemUsingApprovalProps.sol#L73) | Verifies that `redeem` requires token approval. | -| ERC4626-037 | [verify_convertToSharesMustNotRevert](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/MustNotRevertProps.sol#L54) | `convertToShares` must not revert for reasonable values. | +| ID | Name | Invariant tested | +| ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | +| ERC4626-001 | [verify_assetMustNotRevert](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/MustNotRevertProps.sol#L10) | `asset` must not revert. | +| ERC4626-002 | [verify_totalAssetsMustNotRevert](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/MustNotRevertProps.sol#L20) | `totalAssets` must not revert. | +| ERC4626-003 | [verify_convertToAssetsMustNotRevert](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/MustNotRevertProps.sol#L30) | `convertToAssets` must not revert for reasonable values. | +| ERC4626-004 | [verify_convertToSharesMustNotRevert](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/MustNotRevertProps.sol#L54) | `convertToShares` must not revert for reasonable values. | +| ERC4626-005 | [verify_maxDepositMustNotRevert](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/MustNotRevertProps.sol#L71) | `maxDeposit` must not revert. | +| ERC4626-006 | [verify_maxMintMustNotRevert](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/MustNotRevertProps.sol#L79) | `maxMint` must not revert. | +| ERC4626-007 | [verify_maxRedeemMustNotRevert](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/MustNotRevertProps.sol#L87) | `maxRedeem` must not revert. | +| ERC4626-008 | [verify_maxWithdrawMustNotRevert](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/MustNotRevertProps.sol#L99) | `maxWithdraw` must not revert. | +| ERC4626-009 | [verify_redeemViaApprovalProxy](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/RedeemUsingApprovalProps.sol#L10) | Redeem via approval proxy. | +| ERC4626-010 | [verify_withdrawViaApprovalProxy](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/RedeemUsingApprovalProps.sol#L31) | Withdraw via approval proxy. | +| ERC4626-011 | [verify_withdrawRequiresTokenApproval](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/RedeemUsingApprovalProps.sol#L56) | Withdraw requires approval for third parties. | +| ERC4626-012 | [verify_redeemRequiresTokenApproval](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/RedeemUsingApprovalProps.sol#L73) | Redeem requires approval for third parties. | +| ERC4626-013 | [verify_previewDepositRoundingDirection](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/RoundingProps.sol#L9) | checks `previewDeposit` rounding direction | +| ERC4626-014 | [verify_previewMintRoundingDirection](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/RoundingProps.sol#L15) | checks `previewMint` rounding direction | +| ERC4626-015 | [verify_convertToSharesRoundingDirection](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/RoundingProps.sol#L22) | checks `convertToShares` rounding direction | +| ERC4626-016 | [verify_previewRedeemRoundingDirection](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/RoundingProps.sol#L29) | checks `previewRedeem` rounding direction | +| ERC4626-017 | [verify_previewWithdrawRoundingDirection](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/RoundingProps.sol#L35) | checks `previewWithdraw` rounding direction | +| ERC4626-018 | [verify_convertToAssetsRoundingDirection](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/RoundingProps.sol#L42) | checks `convertToAssets` rounding direction | +| ERC4626-019 | [verify_depositRoundingDirection](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/RoundingProps.sol#L48) | checks `deposit` rounding direction | +| ERC4626-020 | [verify_mintRoundingDirection](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/RoundingProps.sol#L54) | checks `mint` rounding direction | +| ERC4626-021 | [verify_withdrawRoundingDirection](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/RoundingProps.sol#L62) | checks `withdraw` rounding direction | +| ERC4626-022 | [verify_redeemRoundingDirection](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/RoundingProps.sol#L70) | checks `redeem` rounding direction | +| ERC4626-023 | [verify_maxDepositIgnoresSenderAssets](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/SenderIndependentProps.sol#L10) | `maxDeposit` must assume the agent has infinite assets. | +| ERC4626-024 | [verify_maxMintIgnoresSenderAssets](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/SenderIndependentProps.sol#L19) | `maxMint` must assume the agent has infinite assets. | +| ERC4626-025 | [verify_previewMintIgnoresSender](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/SenderIndependentProps.sol#L28) | `previewMint` must not be dependent on `msg.sender`. | +| ERC4626-026 | [verify_previewDepositIgnoresSender](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/SenderIndependentProps.sol#L38) | `previewDeposit` must not be dependent on `msg.sender`. | +| ERC4626-027 | [verify_previewWithdrawIgnoresSender](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/SenderIndependentProps.sol#L48) | `previewWithdraw` must not be dependent on `msg.sender`. | +| ERC4626-028 | [verify_previewRedeemIgnoresSender](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/SenderIndependentProps.sol#L63) | `previewRedeem` must not be dependent on `msg.sender`. | +| ERC4626-029 | [verify_depositProperties](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/FunctionalAccountingProps.sol#L11) | `deposit` assets and shares handling. | +| ERC4626-030 | [verify_mintProperties](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/FunctionalAccountingProps.sol#L39) | `mint` assets and shares handling. | +| ERC4626-031 | [verify_redeemProperties](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/FunctionalAccountingProps.sol#L65) | `redeem` assets and shares handling. | +| ERC4626-032 | [verify_withdrawProperties](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/FunctionalAccountingProps.sol#L92) | `withdraw` assets and shares handling. | +| ERC4626-033 | [verify_sharePriceInflationAttack](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/SecurityProps.sol#L12) | Checks for share inflation attacks. | +| ERC4626-034 | [verify_assetDecimalsLessThanVault](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/SecurityProps.sol#L7) | Compares vault decimals with asset decimals. | +| ERC4626-035 | [verify_withdrawRequiresTokenApproval](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/RedeemUsingApprovalProps.sol#L56) | Verifies that `withdraw` requires token approval. | +| ERC4626-036 | [verify_redeemRequiresTokenApproval](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/RedeemUsingApprovalProps.sol#L73) | Verifies that `redeem` requires token approval. | +| ERC4626-037 | [verify_convertToSharesMustNotRevert](https://github.com/crytic/properties/blob/main/contracts/ERC4626/properties/MustNotRevertProps.sol#L54) | `convertToShares` must not revert for reasonable values. | ## ABDKMath64x64 -| ID | Name | Invariant tested | -|---|---|---| -| ABDKMATH-001 | [add_test_commutative](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L274) | Commutative property for addition. | -| ABDKMATH-002 | [add_test_associative](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L283) | Associative property for addition. | -| ABDKMATH-003 | [add_test_identity](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L294) | Identity operation for addition. | -| ABDKMATH-004 | [add_test_values](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L303) | Addition result should increase or decrease depending on operands signs. | -| ABDKMATH-005 | [add_test_range](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L322) | Addition result should be in the valid 64x64-arithmetic range. | -| ABDKMATH-006 | [add_test_maximum_value](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L334) | Addition edge case: maximum value plus zero should be maximum value. | -| ABDKMATH-007 | [add_test_maximum_value_plus_one](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L346) | Addition edge case: maximum value plus one should revert (out of range). | -| ABDKMATH-008 | [add_test_minimum_value](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L356) | Addition edge case: minimum value plus zero should be minimum value. | -| ABDKMATH-009 | [add_test_minimum_value_plus_negative_one](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L368) | Addition edge case: minimum value plus minus one should revert (out of range). | -| ABDKMATH-010 | [sub_test_equivalence_to_addition](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L392) | Subtraction should be equal to addition with opposite sign. | -| ABDKMATH-011 | [sub_test_non_commutative](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L402) | Anti-commutative property for subtraction. | -| ABDKMATH-012 | [sub_test_identity](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L411) | Identity operation for subtraction. | -| ABDKMATH-013 | [sub_test_neutrality](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L420) | Adding and subtracting the same value should not affect original value. | -| ABDKMATH-014 | [sub_test_values](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L433) | Subtraction result should increase or decrease depending on operands signs. | -| ABDKMATH-015 | [sub_test_range](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L451) | Subtraction result should be in the valid 64x64-arithmetic range. | -| ABDKMATH-016 | [sub_test_maximum_value](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L463) | Subtraction edge case: maximum value minus zero should be maximum value. | -| ABDKMATH-017 | [sub_test_maximum_value_minus_neg_one](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L476) | Subtraction edge case: maximum value minus negative one should revert (out of range). | -| ABDKMATH-018 | [sub_test_minimum_value](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L486) | Subtraction edge case: minimum value minus zero should be minimum value. | -| ABDKMATH-019 | [sub_test_minimum_value_minus_one](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L498) | Subtraction edge case: minimum value minus one should revert (out of range). | -| ABDKMATH-020 | [mul_test_commutative](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L522) | Commutative property for multiplication. | -| ABDKMATH-021 | [mul_test_associative](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L531) | Associative property for multiplication. | -| ABDKMATH-022 | [mul_test_distributive](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L548) | Distributive property for multiplication. | -| ABDKMATH-023 | [mul_test_identity](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L565) | Identity operation for multiplication. | -| ABDKMATH-024 | [mul_test_values](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L575) | Multiplication result should increase or decrease depending on operands signs. | -| ABDKMATH-025 | [mul_test_range](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L606) | Multiplication result should be in the valid 64x64-arithmetic range. | -| ABDKMATH-026 | [mul_test_maximum_value](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L618) | Multiplication edge case: maximum value times one should be maximum value | -| ABDKMATH-027 | [mul_test_minimum_value](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L631) | Multiplication edge case: minimum value times one should be minimum value | -| ABDKMATH-028 | [div_test_division_identity](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L659) | Identity operation for division. | -| ABDKMATH-029 | [div_test_negative_divisor](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L678) | Division result sign should change according to divisor sign. | -| ABDKMATH-030 | [div_test_division_num_zero](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L689) | Division with zero numerator should be zero. | -| ABDKMATH-031 | [div_test_values](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L699) | Division result should increase or decrease (in absolute value) depending on divisor's absolute value. | -| ABDKMATH-032 | [div_test_div_by_zero](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L718) | Division edge case: Divisor zero should revert. | -| ABDKMATH-033 | [div_test_maximum_denominator](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L728) | Division edge case: Division result by a large number should be less than one. | -| ABDKMATH-034 | [div_test_maximum_numerator](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L736) | Division edge case: Division result of maximum value should revert if divisor is less than one. | -| ABDKMATH-035 | [div_test_range](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L750) | Division result should be in the valid 64x64-arithmetic range. | -| ABDKMATH-036 | [neg_test_double_negation](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L778) | Double sign negation should be equal to the original operand. | -| ABDKMATH-037 | [neg_test_identity](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L786) | Identity operation for sign negation. | -| ABDKMATH-038 | [neg_test_zero](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L801) | Negation edge case: Negation of zero should be zero. | -| ABDKMATH-039 | [neg_test_maximum](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L809) | Negation edge case: Negation of maximum value minus epsilon should not revert. | -| ABDKMATH-040 | [neg_test_minimum](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L819) | Negation edge case: Negation of minimum value plus epsilon should not revert. | -| ABDKMATH-041 | [abs_test_positive](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L842) | Absolute value should be always positive. | -| ABDKMATH-042 | [abs_test_negative](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L850) | Absolute value of a number and its negation should be equal. | -| ABDKMATH-043 | [abs_test_multiplicativeness](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L859) | Multiplicativeness property for absolute value. | -| ABDKMATH-044 | [abs_test_subadditivity](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L874) | Subadditivity property for absolute value. | -| ABDKMATH-045 | [abs_test_zero](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L889) | Absolute value edge case: absolute value of zero is zero. | -| ABDKMATH-046 | [abs_test_maximum](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L903) | Absolute value edge case: absolute value of maximum value is maximum value. | -| ABDKMATH-047 | [abs_test_minimum](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L916) | Absolute value edge case: absolute value of minimum value is the negation of minimum value. | -| ABDKMATH-048 | [inv_test_double_inverse](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L945) | Result of double inverse should be _close enough_ to the original operand. | -| ABDKMATH-049 | [inv_test_division](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L957) | Inverse should be equivalent to division. | -| ABDKMATH-050 | [inv_test_division_noncommutativity](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L968) | Anticommutative property for inverse operation. | -| ABDKMATH-051 | [inv_test_multiplication](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L981) | Multiplication of inverses should be equal to inverse of multiplication. | -| ABDKMATH-052 | [inv_test_identity](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1003) | Identity property for inverse. | -| ABDKMATH-053 | [inv_test_values](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1018) | Inverse result should be in range (0, 1) if operand is greater than one. | -| ABDKMATH-054 | [inv_test_sign](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1031) | Inverse result should keep operand's sign. | -| ABDKMATH-055 | [inv_test_zero](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1050) | Inverse edge case: Inverse of zero should revert. | -| ABDKMATH-056 | [inv_test_maximum](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1060) | Inverse edge case: Inverse of maximum value should be close to zero. | -| ABDKMATH-057 | [inv_test_minimum](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1073) | Inverse edge case: Inverse of minimum value should be close to zero. | -| ABDKMATH-058 | [avg_test_values_in_range](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1102) | Average result should be between operands. | -| ABDKMATH-059 | [avg_test_one_value](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1114) | Average of the same number twice is the number itself. | -| ABDKMATH-060 | [avg_test_operand_order](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1122) | Average result does not depend on the order of operands. | -| ABDKMATH-061 | [avg_test_maximum](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1137) | Average edge case: Average of maximum value twice is the maximum value. | -| ABDKMATH-062 | [avg_test_minimum](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1151) | Average edge case: Average of minimum value twice is the minimum value. | -| ABDKMATH-063 | [gavg_test_values_in_range](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1181) | Geometric average result should be between operands. | -| ABDKMATH-064 | [gavg_test_one_value](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1197) | Geometric average of the same number twice is the number itself. | -| ABDKMATH-065 | [gavg_test_operand_order](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1205) | Geometric average result does not depend on the order of operands. | -| ABDKMATH-066 | [gavg_test_maximum](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1220) | Geometric average edge case: Average of maximum value twice is the maximum value. | -| ABDKMATH-067 | [gavg_test_minimum](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1234) | Geometric average edge case: Average of minimum value twice is the minimum value. | -| ABDKMATH-068 | [pow_test_zero_exponent](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1264) | Power of zero should be one. | -| ABDKMATH-069 | [pow_test_zero_base](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1272) | Zero to the power of any number should be zero. | -| ABDKMATH-070 | [pow_test_one_exponent](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1282) | Power of one should be equal to the operand. | -| ABDKMATH-071 | [pow_test_base_one](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1290) | One to the power of any number should be one. | -| ABDKMATH-072 | [pow_test_product_same_base](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1298) | Product of powers of the same base property | -| ABDKMATH-073 | [pow_test_power_of_an_exponentiation](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1310) | Power of an exponentiation property | -| ABDKMATH-074 | [pow_test_product_same_base](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1322) | Distributive property for power of a product | -| ABDKMATH-075 | [pow_test_values](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1336) | Power result should increase or decrease (in absolute value) depending on exponent's absolute value. | -| ABDKMATH-076 | [pow_test_sign](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1352) | Power result sign should change according to the exponent sign. | -| ABDKMATH-077 | [pow_test_maximum_base](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1382) | Power edge case: Power of the maximum value should revert if exponent > 1. | -| ABDKMATH-078 | [pow_test_high_exponent](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1394) | Power edge case: Result should be zero if base is small and exponent is large. | -| ABDKMATH-079 | [sqrt_test_inverse_mul](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1418) | Square root inverse as multiplication. | -| ABDKMATH-080 | [sqrt_test_inverse_pow](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1430) | Square root inverse as power. | -| ABDKMATH-081 | [sqrt_test_distributive](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1442) | Square root distributive property respect to multiplication. | -| ABDKMATH-082 | [sqrt_test_zero](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1465) | Square root edge case: square root of zero should be zero. | -| ABDKMATH-083 | [sqrt_test_maximum](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1470) | Square root edge case: square root of maximum value should not revert. | -| ABDKMATH-084 | [sqrt_test_minimum](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1481) | Square root edge case: square root of minimum value should revert (negative). | -| ABDKMATH-085 | [sqrt_test_negative](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1491) | Square root edge case: square root of a negative value should revert. | -| ABDKMATH-086 | [log2_test_distributive_mul](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1518) | Base-2 logarithm distributive property respect to multiplication. | -| ABDKMATH-087 | [log2_test_power](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1538) | Base-2 logarithm of a power property. | -| ABDKMATH-088 | [log2_test_zero](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1555) | Base-2 logarithm edge case: Logarithm of zero should revert. | -| ABDKMATH-089 | [log2_test_maximum](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1565) | Base-2 logarithm edge case: Logarithm of maximum value should not revert. | -| ABDKMATH-090 | [log2_test_negative](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1579) | Base-2 logarithm edge case: Logarithm of a negative value should revert. | -| ABDKMATH-091 | [ln_test_distributive_mul](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1606) | Natural logarithm distributive property respect to multiplication. | -| ABDKMATH-092 | [ln_test_power](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1627) | Natural logarithm of a power property. | -| ABDKMATH-093 | [ln_test_zero](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1644) | Natural logarithm edge case: Logarithm of zero should revert. | -| ABDKMATH-094 | [ln_test_maximum](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1654) | Natural logarithm edge case: Logarithm of maximum value should not revert. | -| ABDKMATH-095 | [ln_test_negative](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1668) | Natural logarithm edge case: Logarithm of a negative value should revert. | -| ABDKMATH-096 | [exp2_test_equivalence_pow](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1695) | Base-2 exponentiation should be equal to power. | -| ABDKMATH-097 | [exp2_test_inverse](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1704) | Base-2 exponentiation inverse function. | -| ABDKMATH-098 | [exp2_test_negative_exponent](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1719) | Base-2 exponentiation with negative exponent should equal the inverse. | -| ABDKMATH-099 | [exp2_test_zero](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1737) | Base-2 exponentiation edge case: exponent zero result should be one. | -| ABDKMATH-100 | [exp2_test_maximum](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1744) | Base-2 exponentiation edge case: exponent maximum value should revert. | -| ABDKMATH-101 | [exp2_test_minimum](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1755) | Base-2 exponentiation edge case: exponent minimum value result should be zero. | -| ABDKMATH-102 | [exp_test_inverse](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1784) | Natural exponentiation inverse function. | -| ABDKMATH-103 | [exp_test_negative_exponent](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1800) | Natural exponentiation with negative exponent should equal the inverse. | -| ABDKMATH-104 | [exp_test_zero](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1818) | Natural exponentiation edge case: exponent zero result should be one. | -| ABDKMATH-105 | [exp_test_maximum](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1825) | Natural exponentiation edge case: exponent maximum value should revert. | -| ABDKMATH-106 | [exp_test_minimum](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1836) | Natural exponentiation edge case: exponent minimum value result should be zero. | +| ID | Name | Invariant tested | +| ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | +| ABDKMATH-001 | [add_test_commutative](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L274) | Commutative property for addition. | +| ABDKMATH-002 | [add_test_associative](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L283) | Associative property for addition. | +| ABDKMATH-003 | [add_test_identity](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L294) | Identity operation for addition. | +| ABDKMATH-004 | [add_test_values](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L303) | Addition result should increase or decrease depending on operands signs. | +| ABDKMATH-005 | [add_test_range](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L322) | Addition result should be in the valid 64x64-arithmetic range. | +| ABDKMATH-006 | [add_test_maximum_value](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L334) | Addition edge case: maximum value plus zero should be maximum value. | +| ABDKMATH-007 | [add_test_maximum_value_plus_one](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L346) | Addition edge case: maximum value plus one should revert (out of range). | +| ABDKMATH-008 | [add_test_minimum_value](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L356) | Addition edge case: minimum value plus zero should be minimum value. | +| ABDKMATH-009 | [add_test_minimum_value_plus_negative_one](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L368) | Addition edge case: minimum value plus minus one should revert (out of range). | +| ABDKMATH-010 | [sub_test_equivalence_to_addition](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L392) | Subtraction should be equal to addition with opposite sign. | +| ABDKMATH-011 | [sub_test_non_commutative](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L402) | Anti-commutative property for subtraction. | +| ABDKMATH-012 | [sub_test_identity](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L411) | Identity operation for subtraction. | +| ABDKMATH-013 | [sub_test_neutrality](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L420) | Adding and subtracting the same value should not affect original value. | +| ABDKMATH-014 | [sub_test_values](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L433) | Subtraction result should increase or decrease depending on operands signs. | +| ABDKMATH-015 | [sub_test_range](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L451) | Subtraction result should be in the valid 64x64-arithmetic range. | +| ABDKMATH-016 | [sub_test_maximum_value](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L463) | Subtraction edge case: maximum value minus zero should be maximum value. | +| ABDKMATH-017 | [sub_test_maximum_value_minus_neg_one](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L476) | Subtraction edge case: maximum value minus negative one should revert (out of range). | +| ABDKMATH-018 | [sub_test_minimum_value](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L486) | Subtraction edge case: minimum value minus zero should be minimum value. | +| ABDKMATH-019 | [sub_test_minimum_value_minus_one](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L498) | Subtraction edge case: minimum value minus one should revert (out of range). | +| ABDKMATH-020 | [mul_test_commutative](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L522) | Commutative property for multiplication. | +| ABDKMATH-021 | [mul_test_associative](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L531) | Associative property for multiplication. | +| ABDKMATH-022 | [mul_test_distributive](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L548) | Distributive property for multiplication. | +| ABDKMATH-023 | [mul_test_identity](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L565) | Identity operation for multiplication. | +| ABDKMATH-024 | [mul_test_values](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L575) | Multiplication result should increase or decrease depending on operands signs. | +| ABDKMATH-025 | [mul_test_range](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L606) | Multiplication result should be in the valid 64x64-arithmetic range. | +| ABDKMATH-026 | [mul_test_maximum_value](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L618) | Multiplication edge case: maximum value times one should be maximum value | +| ABDKMATH-027 | [mul_test_minimum_value](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L631) | Multiplication edge case: minimum value times one should be minimum value | +| ABDKMATH-028 | [div_test_division_identity](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L659) | Identity operation for division. | +| ABDKMATH-029 | [div_test_negative_divisor](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L678) | Division result sign should change according to divisor sign. | +| ABDKMATH-030 | [div_test_division_num_zero](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L689) | Division with zero numerator should be zero. | +| ABDKMATH-031 | [div_test_values](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L699) | Division result should increase or decrease (in absolute value) depending on divisor's absolute value. | +| ABDKMATH-032 | [div_test_div_by_zero](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L718) | Division edge case: Divisor zero should revert. | +| ABDKMATH-033 | [div_test_maximum_denominator](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L728) | Division edge case: Division result by a large number should be less than one. | +| ABDKMATH-034 | [div_test_maximum_numerator](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L736) | Division edge case: Division result of maximum value should revert if divisor is less than one. | +| ABDKMATH-035 | [div_test_range](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L750) | Division result should be in the valid 64x64-arithmetic range. | +| ABDKMATH-036 | [neg_test_double_negation](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L778) | Double sign negation should be equal to the original operand. | +| ABDKMATH-037 | [neg_test_identity](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L786) | Identity operation for sign negation. | +| ABDKMATH-038 | [neg_test_zero](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L801) | Negation edge case: Negation of zero should be zero. | +| ABDKMATH-039 | [neg_test_maximum](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L809) | Negation edge case: Negation of maximum value minus epsilon should not revert. | +| ABDKMATH-040 | [neg_test_minimum](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L819) | Negation edge case: Negation of minimum value plus epsilon should not revert. | +| ABDKMATH-041 | [abs_test_positive](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L842) | Absolute value should be always positive. | +| ABDKMATH-042 | [abs_test_negative](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L850) | Absolute value of a number and its negation should be equal. | +| ABDKMATH-043 | [abs_test_multiplicativeness](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L859) | Multiplicativeness property for absolute value. | +| ABDKMATH-044 | [abs_test_subadditivity](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L874) | Subadditivity property for absolute value. | +| ABDKMATH-045 | [abs_test_zero](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L889) | Absolute value edge case: absolute value of zero is zero. | +| ABDKMATH-046 | [abs_test_maximum](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L903) | Absolute value edge case: absolute value of maximum value is maximum value. | +| ABDKMATH-047 | [abs_test_minimum](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L916) | Absolute value edge case: absolute value of minimum value is the negation of minimum value. | +| ABDKMATH-048 | [inv_test_double_inverse](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L945) | Result of double inverse should be _close enough_ to the original operand. | +| ABDKMATH-049 | [inv_test_division](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L957) | Inverse should be equivalent to division. | +| ABDKMATH-050 | [inv_test_division_noncommutativity](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L968) | Anticommutative property for inverse operation. | +| ABDKMATH-051 | [inv_test_multiplication](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L981) | Multiplication of inverses should be equal to inverse of multiplication. | +| ABDKMATH-052 | [inv_test_identity](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1003) | Identity property for inverse. | +| ABDKMATH-053 | [inv_test_values](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1018) | Inverse result should be in range (0, 1) if operand is greater than one. | +| ABDKMATH-054 | [inv_test_sign](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1031) | Inverse result should keep operand's sign. | +| ABDKMATH-055 | [inv_test_zero](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1050) | Inverse edge case: Inverse of zero should revert. | +| ABDKMATH-056 | [inv_test_maximum](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1060) | Inverse edge case: Inverse of maximum value should be close to zero. | +| ABDKMATH-057 | [inv_test_minimum](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1073) | Inverse edge case: Inverse of minimum value should be close to zero. | +| ABDKMATH-058 | [avg_test_values_in_range](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1102) | Average result should be between operands. | +| ABDKMATH-059 | [avg_test_one_value](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1114) | Average of the same number twice is the number itself. | +| ABDKMATH-060 | [avg_test_operand_order](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1122) | Average result does not depend on the order of operands. | +| ABDKMATH-061 | [avg_test_maximum](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1137) | Average edge case: Average of maximum value twice is the maximum value. | +| ABDKMATH-062 | [avg_test_minimum](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1151) | Average edge case: Average of minimum value twice is the minimum value. | +| ABDKMATH-063 | [gavg_test_values_in_range](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1181) | Geometric average result should be between operands. | +| ABDKMATH-064 | [gavg_test_one_value](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1197) | Geometric average of the same number twice is the number itself. | +| ABDKMATH-065 | [gavg_test_operand_order](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1205) | Geometric average result does not depend on the order of operands. | +| ABDKMATH-066 | [gavg_test_maximum](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1220) | Geometric average edge case: Average of maximum value twice is the maximum value. | +| ABDKMATH-067 | [gavg_test_minimum](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1234) | Geometric average edge case: Average of minimum value twice is the minimum value. | +| ABDKMATH-068 | [pow_test_zero_exponent](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1264) | Power of zero should be one. | +| ABDKMATH-069 | [pow_test_zero_base](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1272) | Zero to the power of any number should be zero. | +| ABDKMATH-070 | [pow_test_one_exponent](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1282) | Power of one should be equal to the operand. | +| ABDKMATH-071 | [pow_test_base_one](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1290) | One to the power of any number should be one. | +| ABDKMATH-072 | [pow_test_product_same_base](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1298) | Product of powers of the same base property | +| ABDKMATH-073 | [pow_test_power_of_an_exponentiation](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1310) | Power of an exponentiation property | +| ABDKMATH-074 | [pow_test_product_same_base](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1322) | Distributive property for power of a product | +| ABDKMATH-075 | [pow_test_values](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1336) | Power result should increase or decrease (in absolute value) depending on exponent's absolute value. | +| ABDKMATH-076 | [pow_test_sign](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1352) | Power result sign should change according to the exponent sign. | +| ABDKMATH-077 | [pow_test_maximum_base](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1382) | Power edge case: Power of the maximum value should revert if exponent > 1. | +| ABDKMATH-078 | [pow_test_high_exponent](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1394) | Power edge case: Result should be zero if base is small and exponent is large. | +| ABDKMATH-079 | [sqrt_test_inverse_mul](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1418) | Square root inverse as multiplication. | +| ABDKMATH-080 | [sqrt_test_inverse_pow](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1430) | Square root inverse as power. | +| ABDKMATH-081 | [sqrt_test_distributive](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1442) | Square root distributive property respect to multiplication. | +| ABDKMATH-082 | [sqrt_test_zero](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1465) | Square root edge case: square root of zero should be zero. | +| ABDKMATH-083 | [sqrt_test_maximum](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1470) | Square root edge case: square root of maximum value should not revert. | +| ABDKMATH-084 | [sqrt_test_minimum](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1481) | Square root edge case: square root of minimum value should revert (negative). | +| ABDKMATH-085 | [sqrt_test_negative](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1491) | Square root edge case: square root of a negative value should revert. | +| ABDKMATH-086 | [log2_test_distributive_mul](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1518) | Base-2 logarithm distributive property respect to multiplication. | +| ABDKMATH-087 | [log2_test_power](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1538) | Base-2 logarithm of a power property. | +| ABDKMATH-088 | [log2_test_zero](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1555) | Base-2 logarithm edge case: Logarithm of zero should revert. | +| ABDKMATH-089 | [log2_test_maximum](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1565) | Base-2 logarithm edge case: Logarithm of maximum value should not revert. | +| ABDKMATH-090 | [log2_test_negative](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1579) | Base-2 logarithm edge case: Logarithm of a negative value should revert. | +| ABDKMATH-091 | [ln_test_distributive_mul](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1606) | Natural logarithm distributive property respect to multiplication. | +| ABDKMATH-092 | [ln_test_power](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1627) | Natural logarithm of a power property. | +| ABDKMATH-093 | [ln_test_zero](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1644) | Natural logarithm edge case: Logarithm of zero should revert. | +| ABDKMATH-094 | [ln_test_maximum](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1654) | Natural logarithm edge case: Logarithm of maximum value should not revert. | +| ABDKMATH-095 | [ln_test_negative](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1668) | Natural logarithm edge case: Logarithm of a negative value should revert. | +| ABDKMATH-096 | [exp2_test_equivalence_pow](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1695) | Base-2 exponentiation should be equal to power. | +| ABDKMATH-097 | [exp2_test_inverse](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1704) | Base-2 exponentiation inverse function. | +| ABDKMATH-098 | [exp2_test_negative_exponent](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1719) | Base-2 exponentiation with negative exponent should equal the inverse. | +| ABDKMATH-099 | [exp2_test_zero](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1737) | Base-2 exponentiation edge case: exponent zero result should be one. | +| ABDKMATH-100 | [exp2_test_maximum](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1744) | Base-2 exponentiation edge case: exponent maximum value should revert. | +| ABDKMATH-101 | [exp2_test_minimum](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1755) | Base-2 exponentiation edge case: exponent minimum value result should be zero. | +| ABDKMATH-102 | [exp_test_inverse](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1784) | Natural exponentiation inverse function. | +| ABDKMATH-103 | [exp_test_negative_exponent](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1800) | Natural exponentiation with negative exponent should equal the inverse. | +| ABDKMATH-104 | [exp_test_zero](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1818) | Natural exponentiation edge case: exponent zero result should be one. | +| ABDKMATH-105 | [exp_test_maximum](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1825) | Natural exponentiation edge case: exponent maximum value should revert. | +| ABDKMATH-106 | [exp_test_minimum](https://github.com/crytic/properties/blob/main/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol#L1836) | Natural exponentiation edge case: exponent minimum value result should be zero. | diff --git a/README.md b/README.md index a7c7f83..124b1f9 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,6 @@ - [Trophies](#trophies) - [How to contribute to this repo?](#how-to-contribute-to-this-repo) - # Properties This repository contains 168 code properties for: @@ -27,6 +26,7 @@ This repository contains 168 code properties for: - [ABDKMath64x64](https://github.com/abdk-consulting/abdk-libraries-solidity/blob/master/ABDKMath64x64.md) fixed-point library invariants ([106 properties](PROPERTIES.md#abdkmath64x64)). The goals of these properties are to: + - Detect vulnerabilities - Ensure adherence to relevant standards - Provide educational guidance for writing invariants @@ -37,8 +37,9 @@ The properties can be used through unit tests or through fuzzing with [Echidna]( 1. Install [Echidna](https://github.com/crytic/echidna#installation). 2. Import the properties into to your project: - - In case of using Hardhat, use: `npm install https://github.com/crytic/properties.git` or `yarn add https://github.com/crytic/properties.git` - - In case of using Foundry, use: `forge install crytic/properties` + + - In case of using Hardhat, use: `npm install https://github.com/crytic/properties.git` or `yarn add https://github.com/crytic/properties.git` + - In case of using Foundry, use: `forge install crytic/properties` 3. According to tests required, go the the specific sections: - [ERC20 tests](#erc20-tests) @@ -48,6 +49,7 @@ The properties can be used through unit tests or through fuzzing with [Echidna]( ### ERC20 tests To test an ERC20 token, follow these steps: + 1. [Integration](#integration) 2. [Configuration](#configuration) 3. [Run](#run) @@ -55,9 +57,11 @@ To test an ERC20 token, follow these steps: You can see the output for a [compliant token](#example-output-for-a-compliant-token), and [non compliant token](#example-output-for-a-non-compliant-token). #### Integration -Decide if you want to do internal or external testing. Both approaches have advantanges and disadvantages, you can check more information about them [here](https://secure-contracts.com/program-analysis/echidna/basic/common-testing-approaches.html). + +Decide if you want to do internal or external testing. Both approaches have advantanges and disadvantages, you can check more information about them [here](https://secure-contracts.com/program-analysis/echidna/basic/common-testing-approaches.html). For internal testing, create a new Solidity file containing the `CryticERC20InternalHarness` contract. `USER1`, `USER2` and `USER3` constants are initialized by default in `PropertiesConstants` contract to be the addresses from where echidna sends transactions, and `INITIAL_BALANCE` is by default `1000e18`: + ```Solidity pragma solidity ^0.8.0; import "@crytic/properties/contracts/ERC20/internal/properties/ERC20BasicProperties.sol"; @@ -86,7 +90,7 @@ import {CryticERC20ExternalBasicProperties} from "@crytic/properties/contracts/E import {PropertiesConstants} from "@crytic/properties/contracts/util/PropertiesConstants.sol"; -contract CryticERC20ExternalHarness is CryticERC20ExternalBasicProperties { +contract CryticERC20ExternalHarness is CryticERC20ExternalBasicProperties { constructor() { // Deploy ERC20 token = ITokenMock(address(new CryticTokenMock())); @@ -110,6 +114,7 @@ contract CryticTokenMock is MyToken, PropertiesConstants { ``` #### Configuration + Create the following Echidna config file ```yaml @@ -128,21 +133,23 @@ multi-abi: true To perform more than one test, save the files with a descriptive path, to identify what test each file or corpus belongs to. For these examples, we use `tests/crytic/erc20/echidna-internal.yaml` and `tests/crytic/erc20/echidna-external.yaml` for the Echidna tests for ERC20. We recommended to modify the `corpusDir` for external tests accordingly. -The above configuration will start Echidna in assertion mode. Contract will be deployed from address `0x10000`, and transactions will be sent from the owner and two different users (`0x20000` and `0x30000`). There is an initial limit of `100000` tests, but depending on the token code complexity, this can be increased. Finally, once Echidna finishes the fuzzing campaign, corpus and coverage results will be available in the `tests/crytic/erc20/echidna-corpus-internal` directory. - +The above configuration will start Echidna in assertion mode. Contract will be deployed from address `0x10000`, and transactions will be sent from the owner and two different users (`0x20000` and `0x30000`). There is an initial limit of `100000` tests, but depending on the token code complexity, this can be increased. Finally, once Echidna finishes the fuzzing campaign, corpus and coverage results will be available in the `tests/crytic/erc20/echidna-corpus-internal` directory. #### Run -Run Echidna: -- For internal testing: `echidna-test . --contract CryticERC20InternalHarness --config tests/crytic/erc20/echidna-internal.yaml` -- For external testing: `echidna-test . --contract CryticERC20ExternalHarness --config tests/crytic/erc20/echidna-external.yaml` - + +Run Echidna: + +- For internal testing: `echidna-test . --contract CryticERC20InternalHarness --config tests/crytic/erc20/echidna-internal.yaml` +- For external testing: `echidna-test . --contract CryticERC20ExternalHarness --config tests/crytic/erc20/echidna-external.yaml` + Finally, inspect the coverage report in `tests/crytic/erc20/echidna-corpus-internal` or `tests/crytic/erc20/echidna-corpus-external` when it finishes. #### Example: Output for a compliant token If the token under test is compliant and no properties will fail during fuzzing, the Echidna output should be similar to the screen below: + ``` -$ echidna-test . --contract CryticERC20InternalHarness --config tests/echidna.config.yaml +$ echidna-test . --contract CryticERC20InternalHarness --config tests/echidna.config.yaml Loaded total of 23 transactions from corpus/coverage Analyzing contract: contracts/ERC20/CryticERC20InternalHarness.sol:CryticERC20InternalHarness name(): passed! 🎉 @@ -156,6 +163,7 @@ totalSupply(): passed! 🎉 #### Example: Output for a non-compliant token For this example, the ExampleToken's approval function was modified to perform no action: + ``` function approve(address spender, uint256 amount) public virtual override(ERC20) returns (bool) { // do nothing @@ -164,6 +172,7 @@ function approve(address spender, uint256 amount) public virtual override(ERC20) ``` In this case, the Echidna output should be similar to the screen below, notice that all functions that rely on `approve()` to work correctly will have their assertions broken, and will report the situation. + ``` $ echidna-test . --contract CryticERC20ExternalHarness --config tests/echidna.config.yaml Loaded total of 25 transactions from corpus/coverage @@ -172,7 +181,7 @@ name(): passed! 🎉 test_ERC20_transferFromAndBurn(): passed! 🎉 approve(address,uint256): passed! 🎉 ... -test_ERC20_setAllowance(): failed!💥 +test_ERC20_setAllowance(): failed!💥 Call sequence: test_ERC20_setAllowance() @@ -183,11 +192,11 @@ Event sequence: Panic(1), AssertEqFail("Equal assertion failed. Message: Failed ### ERC4626 Tests To test an ERC4626 token, follow these steps: + 1. [Integration](#integration-1) 2. [Configuration](#configuration-1) 3. [Run](#run-1) - #### Integration Create a new Solidity file containing the `CryticERC4626Harness` contract. Make sure it properly initializes your ERC4626 vault with a test token (`TestERC20Token`): @@ -229,6 +238,7 @@ If you are using Foundry: ``` #### Configuration + Create a minimal Echidna config file (e.g. `tests/echidna.config.yaml`) ```yaml @@ -240,12 +250,13 @@ sender: ["0x10000"] ``` #### Run + Run the test suite using `echidna-test . --contract CryticERC4626Harness --config tests/echidna.config.yaml` and inspect the coverage report in `tests/echidna-corpus` when it finishes. Example repositories are available for [Hardhat](tests/ERC4626/hardhat) and [Foundry](tests/ERC4626/foundry). Once things are up and running, consider adding internal testing methods to your Vault ABI to allow testing special edge case properties like rounding. For more info, see the [ERC4626 readme](contracts/ERC4626/README.md#adding-internal-test-methods). - + ### ABDKMath64x64 tests The Solidity smart contract programming language does not have any inbuilt feature for working with decimal numbers, so for contracts dealing with non-integer values, a third party solution is needed. [ABDKMath64x64](https://github.com/abdk-consulting/abdk-libraries-solidity) is a fixed-point arithmetic Solidity library that operates on 64.64-bit numbers. @@ -255,6 +266,7 @@ A 64.64-bit fixed-point number is a data type that consists of a sign bit, a 63- ABDKMath64x64 library implements [19 arithmetic operations](https://github.com/abdk-consulting/abdk-libraries-solidity/blob/master/ABDKMath64x64.md#simple-arithmetic "19 arithmetic operations") using fixed-point numbers and [6 conversion functions](https://github.com/abdk-consulting/abdk-libraries-solidity/blob/master/ABDKMath64x64.md#conversions "6 conversion functions") between integer types and fixed-point types. We provide a number of tests related with fundamental mathematical properties of the floating point numbers. To include these tests into your repository, follow these steps: + 1. [Integration](#integration-2) 2. [Run](#run-2) @@ -272,8 +284,8 @@ contract CryticABDKMath64x64Harness is CryticABDKMath64x64PropertyTests { ``` #### Run -Run the test suite using `echidna-test . --contract CryticABDKMath64x64Harness --seq-len 1 --test-mode assertion --corpus-dir tests/echidna-corpus` and inspect the coverage report in `tests/echidna-corpus` when it finishes. +Run the test suite using `echidna-test . --contract CryticABDKMath64x64Harness --seq-len 1 --test-mode assertion --corpus-dir tests/echidna-corpus` and inspect the coverage report in `tests/echidna-corpus` when it finishes. ## Additional resources @@ -281,12 +293,12 @@ Run the test suite using `echidna-test . --contract CryticABDKMath64x64Harness - - Our [EmpireSlacking](https://empireslacking.herokuapp.com/) slack server, channel #ethereum - Watch our [fuzzing workshop](https://www.youtube.com/watch?v=QofNQxW_K08&list=PLciHOL_J7Iwqdja9UH4ZzE8dP1IxtsBXI) - # Helper functions The repository provides a collection of functions and events meant to simplify the debugging and testing of assertions in Echidna. Commonly used functions, such as integer clamping or logging for different types are available in [contracts/util/PropertiesHelper.sol](contracts/util/PropertiesHelper.sol). Available helpers: + - `LogXxx`: Events that can be used to log values in fuzzing tests. `string`, `uint256` and `address` loggers are provided. In Echidna's assertion mode, when an assertion violation is detected, all events emitted in the call sequence are printed. - `assertXxx`: Asserts that a condition is met, logging violations. Assertions for equality, non-equality, greater-than, greater-than-or-equal, less-than and less-than-or-equal comparisons are provided, and user-provided messages are supported for logging. - `clampXxx`: Limits an `int256` or `uint256` to a certain range. Clamps for less-than, less-than-or-equal, greater-than, greater-than-or-equal, and range are provided. @@ -303,25 +315,22 @@ pragma solidity ^0.8.0; import "@crytic/properties/contracts/util/PropertiesHelper.sol"; contract TestProperties is PropertiesAsserts { - - // ... - - function test_some_invariant(uint256 someValue) public { - // ... + // ... - LogUint256("someValue is: ", someValue); + function test_some_invariant(uint256 someValue) public { + // ... - // ... + LogUint256("someValue is: ", someValue); - assert(fail); + // ... - // ... - } + assert(fail); // ... + } + // ... } - ``` ### Assertions @@ -334,28 +343,24 @@ pragma solidity ^0.8.0; import "@crytic/properties/contracts/util/PropertiesHelper.sol"; contract TestProperties is PropertiesAsserts { - - // ... - - function test_some_invariant(uint256 someValue) public { - // ... + // ... - assertEq(someValue, 25, "someValue doesn't have the correct value"); + function test_some_invariant(uint256 someValue) public { + // ... - // ... - } + assertEq(someValue, 25, "someValue doesn't have the correct value"); // ... + } + // ... } - ``` In case this assertion fails, for example if `someValue` is 30, the following will be printed in Echidna: `Invalid: 30!=25, reason: someValue doesn't have the correct value` - ### Clamping Assure that a function's fuzzed parameter is in a certain range: @@ -366,22 +371,19 @@ pragma solidity ^0.8.0; import "@crytic/properties/contracts/util/PropertiesHelper.sol"; contract TestProperties is PropertiesAsserts { + int256 constant MAX_VALUE = 2 ** 160; + int256 constant MIN_VALUE = -2 ** 24; - int256 constant MAX_VALUE = 2**160; - int256 constant MIN_VALUE = -2**24; - - // ... - - function test_some_invariant(int256 someValue) public { - someValue = clampBetween(someValue, MIN_VALUE, MAX_VALUE); + // ... - // ... - } + function test_some_invariant(int256 someValue) public { + someValue = clampBetween(someValue, MIN_VALUE, MAX_VALUE); // ... + } + // ... } - ``` # HEVM cheat codes support @@ -400,23 +402,20 @@ pragma solidity ^0.8.0; import "@crytic/properties/contracts/util/Hevm.sol"; contract TestProperties { - - // ... + // ... - function test_some_invariant(uint256 someValue) public { - // ... - - hevm.prank(newSender); - otherContract.someFunction(someValue); // This call's msg.sender will be newSender - otherContract.someFunction(someValue); // This call's msg.sender will be address(this) + function test_some_invariant(uint256 someValue) public { + // ... - // ... - } + hevm.prank(newSender); + otherContract.someFunction(someValue); // This call's msg.sender will be newSender + otherContract.someFunction(someValue); // This call's msg.sender will be address(this) // ... + } + // ... } - ``` # Trophies @@ -424,4 +423,5 @@ contract TestProperties { A list of security vulnerabilities that were found using the properties can be found on the [trophies page](Trophies.md#properties-trophies). # How to contribute to this repo? -Contributions are welcome! You can read more about the contribution guidelines and directory structure in the [CONTRIBUTING.md](CONTRIBUTING.md) file. + +Contributions are welcome! You can read more about the contribution guidelines and directory structure in the [CONTRIBUTING.md](CONTRIBUTING.md) file. diff --git a/Trophies.md b/Trophies.md index 3699696..75a6d26 100644 --- a/Trophies.md +++ b/Trophies.md @@ -4,7 +4,7 @@ The following lists security vulnerabilities that were found using the propertie If you found a security vulnerability using crytic/properties, please submit a PR with the relevant information. -| Project | Vulnerability | Date | -|--|--|--| -[Pods Finance](https://github.com/pods-finance/yield-contracts/blob/main/audits/2022-11-15_OpenZeppelin_Pods.pdf) | C-03 The vault can be drained one share at a time | March 2023 -[Pods Finance](https://github.com/pods-finance/yield-contracts/blob/main/audits/2022-11-15_OpenZeppelin_Pods.pdf) | H-03 Rounding up in minting shares | March 2023 \ No newline at end of file +| Project | Vulnerability | Date | +| ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------------- | ---------- | +| [Pods Finance](https://github.com/pods-finance/yield-contracts/blob/main/audits/2022-11-15_OpenZeppelin_Pods.pdf) | C-03 The vault can be drained one share at a time | March 2023 | +| [Pods Finance](https://github.com/pods-finance/yield-contracts/blob/main/audits/2022-11-15_OpenZeppelin_Pods.pdf) | H-03 Rounding up in minting shares | March 2023 | diff --git a/contracts/ERC20/external/ERC20ExternalPropertyTests.sol b/contracts/ERC20/external/ERC20ExternalPropertyTests.sol index 1570cb6..f2f535a 100644 --- a/contracts/ERC20/external/ERC20ExternalPropertyTests.sol +++ b/contracts/ERC20/external/ERC20ExternalPropertyTests.sol @@ -8,12 +8,16 @@ import {CryticERC20ExternalMintableProperties} from "./properties/ERC20ExternalM import {CryticERC20ExternalPausableProperties} from "./properties/ERC20ExternalPausableProperties.sol"; import {CryticERC20ExternalIncreaseAllowanceProperties} from "./properties/ERC20ExternalIncreaseAllowanceProperties.sol"; -contract CryticERC20ExternalPropertyTests is CryticERC20ExternalBasicProperties, CryticERC20ExternalIncreaseAllowanceProperties, CryticERC20ExternalBurnableProperties, CryticERC20ExternalMintableProperties, CryticERC20ExternalPausableProperties { - +contract CryticERC20ExternalPropertyTests is + CryticERC20ExternalBasicProperties, + CryticERC20ExternalIncreaseAllowanceProperties, + CryticERC20ExternalBurnableProperties, + CryticERC20ExternalMintableProperties, + CryticERC20ExternalPausableProperties +{ constructor() { // Deploy ERC20, mint initial balance to users (deployer is address(this)) // If the token is mintable or burnable, the argument must be true. False otherwise. token = ITokenMock(address(new TokenMock(true))); } - } diff --git a/contracts/ERC20/external/ExampleToken.sol b/contracts/ERC20/external/ExampleToken.sol index 6458891..fd359a3 100644 --- a/contracts/ERC20/external/ExampleToken.sol +++ b/contracts/ERC20/external/ExampleToken.sol @@ -20,12 +20,11 @@ contract ExampleToken is ERC20, ERC20Burnable, Pausable, Ownable { _mint(to, amount); } - function _beforeTokenTransfer(address from, address to, uint256 amount) - internal - virtual - whenNotPaused - override - { + function _beforeTokenTransfer( + address from, + address to, + uint256 amount + ) internal virtual override whenNotPaused { super._beforeTokenTransfer(from, to, amount); } } @@ -41,7 +40,10 @@ contract ExampleTokenNonCompliant is ERC20, ERC20Burnable, Pausable, Ownable { _unpause(); } - function approve(address, uint256) public virtual override(ERC20) returns (bool) { + function approve( + address, + uint256 + ) public virtual override(ERC20) returns (bool) { // do nothing return true; } @@ -50,12 +52,11 @@ contract ExampleTokenNonCompliant is ERC20, ERC20Burnable, Pausable, Ownable { _mint(to, amount); } - function _beforeTokenTransfer(address from, address to, uint256 amount) - internal - virtual - whenNotPaused - override - { + function _beforeTokenTransfer( + address from, + address to, + uint256 amount + ) internal virtual override whenNotPaused { super._beforeTokenTransfer(from, to, amount); } -} \ No newline at end of file +} diff --git a/contracts/ERC20/external/TokenMock.sol b/contracts/ERC20/external/TokenMock.sol index 4920ab6..e6fa9c7 100644 --- a/contracts/ERC20/external/TokenMock.sol +++ b/contracts/ERC20/external/TokenMock.sol @@ -4,10 +4,10 @@ import "../../util/PropertiesConstants.sol"; import "./ExampleToken.sol"; contract TokenMock is ExampleToken, PropertiesConstants { - bool public isMintableOrBurnable; uint256 public initialSupply; - constructor (bool _isMintableOrBurnable) { + + constructor(bool _isMintableOrBurnable) { _mint(USER1, INITIAL_BALANCE); _mint(USER2, INITIAL_BALANCE); _mint(USER3, INITIAL_BALANCE); @@ -16,5 +16,4 @@ contract TokenMock is ExampleToken, PropertiesConstants { initialSupply = totalSupply(); isMintableOrBurnable = _isMintableOrBurnable; } - } diff --git a/contracts/ERC20/external/echidna.config.yaml b/contracts/ERC20/external/echidna.config.yaml index 4603034..38ed5ec 100644 --- a/contracts/ERC20/external/echidna.config.yaml +++ b/contracts/ERC20/external/echidna.config.yaml @@ -1,7 +1,7 @@ -coverage: true +coverage: true multi-abi: true corpusDir: "corpus" testMode: assertion testLimit: 100000 deployer: "0x10000" -sender: ["0x10000", "0x20000", "0x30000"] \ No newline at end of file +sender: ["0x10000", "0x20000", "0x30000"] diff --git a/contracts/ERC20/external/properties/ERC20ExternalBasicProperties.sol b/contracts/ERC20/external/properties/ERC20ExternalBasicProperties.sol index e6c634d..c984c0a 100644 --- a/contracts/ERC20/external/properties/ERC20ExternalBasicProperties.sol +++ b/contracts/ERC20/external/properties/ERC20ExternalBasicProperties.sol @@ -2,10 +2,10 @@ pragma solidity ^0.8.0; import {CryticERC20ExternalTestBase} from "../util/ERC20ExternalTestBase.sol"; -abstract contract CryticERC20ExternalBasicProperties is CryticERC20ExternalTestBase { - constructor() { - - } +abstract contract CryticERC20ExternalBasicProperties is + CryticERC20ExternalTestBase +{ + constructor() {} //////////////////////////////////////// // Properties @@ -13,23 +13,42 @@ abstract contract CryticERC20ExternalBasicProperties is CryticERC20ExternalTestB // Total supply should change only by means of mint or burn function test_ERC20external_constantSupply() public virtual { require(!token.isMintableOrBurnable()); - assertEq(token.initialSupply(), token.totalSupply(), "Token supply was modified"); + assertEq( + token.initialSupply(), + token.totalSupply(), + "Token supply was modified" + ); } // User balance must not exceed total supply function test_ERC20external_userBalanceNotHigherThanSupply() public { - assertLte(token.balanceOf(msg.sender), token.totalSupply(), "User balance higher than total supply"); + assertLte( + token.balanceOf(msg.sender), + token.totalSupply(), + "User balance higher than total supply" + ); } // Sum of users balance must not exceed total supply function test_ERC20external_userBalancesLessThanTotalSupply() public { - uint256 sumBalances = token.balanceOf(address(this)) + token.balanceOf(USER1) + token.balanceOf(USER2) + token.balanceOf(USER3); - assertLte(sumBalances, token.totalSupply(), "Sum of user balances are greater than total supply"); + uint256 sumBalances = token.balanceOf(address(this)) + + token.balanceOf(USER1) + + token.balanceOf(USER2) + + token.balanceOf(USER3); + assertLte( + sumBalances, + token.totalSupply(), + "Sum of user balances are greater than total supply" + ); } // Address zero should have zero balance function test_ERC20external_zeroAddressBalance() public { - assertEq(token.balanceOf(address(0)), 0, "Address zero balance not equal to zero"); + assertEq( + token.balanceOf(address(0)), + 0, + "Address zero balance not equal to zero" + ); } // Transfers to zero address should not be allowed @@ -42,13 +61,21 @@ abstract contract CryticERC20ExternalBasicProperties is CryticERC20ExternalTestB } // Transfers to zero address should not be allowed - function test_ERC20external_transferFromToZeroAddress(uint256 value) public { + function test_ERC20external_transferFromToZeroAddress( + uint256 value + ) public { uint256 balance_sender = token.balanceOf(msg.sender); uint256 allowance = token.allowance(msg.sender, address(this)); require(balance_sender > 0 && allowance > 0); - uint256 maxValue = balance_sender >= allowance ? allowance : balance_sender; - - bool r = token.transferFrom(msg.sender, address(0), value % (maxValue + 1)); + uint256 maxValue = balance_sender >= allowance + ? allowance + : balance_sender; + + bool r = token.transferFrom( + msg.sender, + address(0), + value % (maxValue + 1) + ); assertWithMsg(r == false, "Successful transferFrom to address zero"); } @@ -57,11 +84,21 @@ abstract contract CryticERC20ExternalBasicProperties is CryticERC20ExternalTestB uint256 balance_sender = token.balanceOf(msg.sender); uint256 allowance = token.allowance(msg.sender, address(this)); require(balance_sender > 0 && allowance > 0); - uint256 maxValue = balance_sender >= allowance ? allowance : balance_sender; - - bool r = token.transferFrom(msg.sender, msg.sender, value % (maxValue + 1)); + uint256 maxValue = balance_sender >= allowance + ? allowance + : balance_sender; + + bool r = token.transferFrom( + msg.sender, + msg.sender, + value % (maxValue + 1) + ); assertWithMsg(r == true, "Failed self transferFrom"); - assertEq(balance_sender, token.balanceOf(msg.sender), "Self transferFrom breaks accounting"); + assertEq( + balance_sender, + token.balanceOf(msg.sender), + "Self transferFrom breaks accounting" + ); } // Self transfers should not break accounting @@ -71,20 +108,37 @@ abstract contract CryticERC20ExternalBasicProperties is CryticERC20ExternalTestB bool r = token.transfer(address(this), value % (balance_sender + 1)); assertWithMsg(r == true, "Failed self transfer"); - assertEq(balance_sender, token.balanceOf(address(this)), "Self transfer breaks accounting"); + assertEq( + balance_sender, + token.balanceOf(address(this)), + "Self transfer breaks accounting" + ); } // Transfers for more than available balance should not be allowed - function test_ERC20external_transferFromMoreThanBalance(address target) public { + function test_ERC20external_transferFromMoreThanBalance( + address target + ) public { uint256 balance_sender = token.balanceOf(msg.sender); uint256 balance_receiver = token.balanceOf(target); uint256 allowance = token.allowance(msg.sender, address(this)); require(balance_sender > 0 && allowance > balance_sender); - - bool r = token.transferFrom(msg.sender, target, balance_sender+1); - assertWithMsg(r == false, "Successful transferFrom for more than account balance"); - assertEq(token.balanceOf(msg.sender), balance_sender, "TransferFrom for more than balance modified source balance"); - assertEq(token.balanceOf(target), balance_receiver, "TransferFrom for more than balance modified target balance"); + + bool r = token.transferFrom(msg.sender, target, balance_sender + 1); + assertWithMsg( + r == false, + "Successful transferFrom for more than account balance" + ); + assertEq( + token.balanceOf(msg.sender), + balance_sender, + "TransferFrom for more than balance modified source balance" + ); + assertEq( + token.balanceOf(target), + balance_receiver, + "TransferFrom for more than balance modified target balance" + ); } // Transfers for more than available balance should not be allowed @@ -93,10 +147,21 @@ abstract contract CryticERC20ExternalBasicProperties is CryticERC20ExternalTestB uint256 balance_receiver = token.balanceOf(target); require(balance_sender > 0); - bool r = token.transfer(target, balance_sender+1); - assertWithMsg(r == false, "Successful transfer for more than account balance"); - assertEq(token.balanceOf(address(this)), balance_sender, "Transfer for more than balance modified source balance"); - assertEq(token.balanceOf(target), balance_receiver, "Transfer for more than balance modified target balance"); + bool r = token.transfer(target, balance_sender + 1); + assertWithMsg( + r == false, + "Successful transfer for more than account balance" + ); + assertEq( + token.balanceOf(address(this)), + balance_sender, + "Transfer for more than balance modified source balance" + ); + assertEq( + token.balanceOf(target), + balance_receiver, + "Transfer for more than balance modified target balance" + ); } // Zero amount transfers should not break accounting @@ -107,8 +172,16 @@ abstract contract CryticERC20ExternalBasicProperties is CryticERC20ExternalTestB bool r = token.transfer(target, 0); assertWithMsg(r == true, "Zero amount transfer failed"); - assertEq(token.balanceOf(address(this)), balance_sender, "Zero amount transfer modified source balance"); - assertEq(token.balanceOf(target), balance_receiver, "Zero amount transfer modified target balance"); + assertEq( + token.balanceOf(address(this)), + balance_sender, + "Zero amount transfer modified source balance" + ); + assertEq( + token.balanceOf(target), + balance_receiver, + "Zero amount transfer modified target balance" + ); } // Zero amount transfers should not break accounting @@ -120,12 +193,23 @@ abstract contract CryticERC20ExternalBasicProperties is CryticERC20ExternalTestB bool r = token.transferFrom(msg.sender, target, 0); assertWithMsg(r == true, "Zero amount transferFrom failed"); - assertEq(token.balanceOf(msg.sender), balance_sender, "Zero amount transferFrom modified source balance"); - assertEq(token.balanceOf(target), balance_receiver, "Zero amount transferFrom modified target balance"); + assertEq( + token.balanceOf(msg.sender), + balance_sender, + "Zero amount transferFrom modified source balance" + ); + assertEq( + token.balanceOf(target), + balance_receiver, + "Zero amount transferFrom modified target balance" + ); } // Transfers should update accounting correctly - function test_ERC20external_transfer(address target, uint256 amount) public { + function test_ERC20external_transfer( + address target, + uint256 amount + ) public { require(target != address(this)); uint256 balance_sender = token.balanceOf(address(this)); uint256 balance_receiver = token.balanceOf(target); @@ -134,12 +218,23 @@ abstract contract CryticERC20ExternalBasicProperties is CryticERC20ExternalTestB bool r = token.transfer(target, transfer_value); assertWithMsg(r == true, "transfer failed"); - assertEq(token.balanceOf(address(this)), balance_sender-transfer_value, "Wrong source balance after transfer"); - assertEq(token.balanceOf(target), balance_receiver+transfer_value, "Wrong target balance after transfer"); + assertEq( + token.balanceOf(address(this)), + balance_sender - transfer_value, + "Wrong source balance after transfer" + ); + assertEq( + token.balanceOf(target), + balance_receiver + transfer_value, + "Wrong target balance after transfer" + ); } // Transfers should update accounting correctly - function test_ERC20external_transferFrom(address target, uint256 amount) public { + function test_ERC20external_transferFrom( + address target, + uint256 amount + ) public { require(target != address(this)); require(target != msg.sender); uint256 balance_sender = token.balanceOf(msg.sender); @@ -150,30 +245,59 @@ abstract contract CryticERC20ExternalBasicProperties is CryticERC20ExternalTestB bool r = token.transferFrom(msg.sender, target, transfer_value); assertWithMsg(r == true, "transfer failed"); - assertEq(token.balanceOf(msg.sender), balance_sender - transfer_value, "Wrong source balance after transferFrom"); - assertEq(token.balanceOf(target), balance_receiver + transfer_value, "Wrong target balance after transferFrom"); + assertEq( + token.balanceOf(msg.sender), + balance_sender - transfer_value, + "Wrong source balance after transferFrom" + ); + assertEq( + token.balanceOf(target), + balance_receiver + transfer_value, + "Wrong target balance after transferFrom" + ); } // Approve should set correct allowances - function test_ERC20external_setAllowance(address target, uint256 amount) public { + function test_ERC20external_setAllowance( + address target, + uint256 amount + ) public { bool r = token.approve(target, amount); assertWithMsg(r == true, "Failed to set allowance via approve"); - assertEq(token.allowance(address(this), target), amount, "Allowance not set correctly"); + assertEq( + token.allowance(address(this), target), + amount, + "Allowance not set correctly" + ); } // Approve should set correct allowances - function test_ERC20external_setAllowanceTwice(address target, uint256 amount) public { + function test_ERC20external_setAllowanceTwice( + address target, + uint256 amount + ) public { bool r = token.approve(target, amount); assertWithMsg(r == true, "Failed to set allowance via approve"); - assertEq(token.allowance(address(this), target), amount, "Allowance not set correctly"); + assertEq( + token.allowance(address(this), target), + amount, + "Allowance not set correctly" + ); - r = token.approve(target, amount/2); + r = token.approve(target, amount / 2); assertWithMsg(r == true, "Failed to set allowance via approve"); - assertEq(token.allowance(address(this), target), amount/2, "Allowance not set correctly"); + assertEq( + token.allowance(address(this), target), + amount / 2, + "Allowance not set correctly" + ); } // TransferFrom should decrease allowance - function test_ERC20external_spendAllowanceAfterTransfer(address target, uint256 amount) public { + function test_ERC20external_spendAllowanceAfterTransfer( + address target, + uint256 amount + ) public { require(target != address(this) && target != address(0)); require(target != msg.sender); uint256 balance_sender = token.balanceOf(msg.sender); @@ -186,8 +310,11 @@ abstract contract CryticERC20ExternalBasicProperties is CryticERC20ExternalTestB // Some implementations take an allowance of 2**256-1 as infinite, and therefore don't update if (current_allowance != type(uint256).max) { - assertEq(token.allowance(msg.sender, address(this)), current_allowance - transfer_value, "Allowance not updated correctly"); + assertEq( + token.allowance(msg.sender, address(this)), + current_allowance - transfer_value, + "Allowance not updated correctly" + ); } } - } diff --git a/contracts/ERC20/external/properties/ERC20ExternalBurnableProperties.sol b/contracts/ERC20/external/properties/ERC20ExternalBurnableProperties.sol index 6baf269..cef2f15 100644 --- a/contracts/ERC20/external/properties/ERC20ExternalBurnableProperties.sol +++ b/contracts/ERC20/external/properties/ERC20ExternalBurnableProperties.sol @@ -2,11 +2,10 @@ pragma solidity ^0.8.0; import "../util/ERC20ExternalTestBase.sol"; -abstract contract CryticERC20ExternalBurnableProperties is CryticERC20ExternalTestBase { - - constructor() { - - } +abstract contract CryticERC20ExternalBurnableProperties is + CryticERC20ExternalTestBase +{ + constructor() {} //////////////////////////////////////// // Properties @@ -16,11 +15,19 @@ abstract contract CryticERC20ExternalBurnableProperties is CryticERC20ExternalTe uint256 balance_sender = token.balanceOf(address(this)); uint256 supply = token.totalSupply(); require(balance_sender > 0); - uint256 burn_amount = amount % (balance_sender+1); + uint256 burn_amount = amount % (balance_sender + 1); token.burn(burn_amount); - assertEq(token.balanceOf(address(this)), balance_sender - burn_amount, "Source balance incorrect after burn"); - assertEq(token.totalSupply(), supply-burn_amount, "Total supply incorrect after burn"); + assertEq( + token.balanceOf(address(this)), + balance_sender - burn_amount, + "Source balance incorrect after burn" + ); + assertEq( + token.totalSupply(), + supply - burn_amount, + "Total supply incorrect after burn" + ); } // Burn should update user balance and total supply @@ -29,11 +36,19 @@ abstract contract CryticERC20ExternalBurnableProperties is CryticERC20ExternalTe uint256 allowance = token.allowance(msg.sender, address(this)); require(balance_sender > 0 && allowance > balance_sender); uint256 supply = token.totalSupply(); - uint256 burn_amount = amount % (balance_sender+1); + uint256 burn_amount = amount % (balance_sender + 1); token.burnFrom(msg.sender, burn_amount); - assertEq(token.balanceOf(msg.sender), balance_sender - burn_amount, "Source balance incorrect after burnFrom"); - assertEq(token.totalSupply(), supply-burn_amount, "Total supply incorrect after burnFrom"); + assertEq( + token.balanceOf(msg.sender), + balance_sender - burn_amount, + "Source balance incorrect after burnFrom" + ); + assertEq( + token.totalSupply(), + supply - burn_amount, + "Total supply incorrect after burnFrom" + ); } // burnFrom should update allowance @@ -41,14 +56,17 @@ abstract contract CryticERC20ExternalBurnableProperties is CryticERC20ExternalTe uint256 balance_sender = token.balanceOf(msg.sender); uint256 current_allowance = token.allowance(msg.sender, address(this)); require(balance_sender > 0 && current_allowance > balance_sender); - uint256 burn_amount = amount % (balance_sender+1); + uint256 burn_amount = amount % (balance_sender + 1); token.burnFrom(msg.sender, burn_amount); // Some implementations take an allowance of 2**256-1 as infinite, and therefore don't update if (current_allowance != type(uint256).max) { - assertEq(token.allowance(msg.sender, address(this)), current_allowance - burn_amount, "Allowance not updated correctly"); + assertEq( + token.allowance(msg.sender, address(this)), + current_allowance - burn_amount, + "Allowance not updated correctly" + ); } } - } diff --git a/contracts/ERC20/external/properties/ERC20ExternalIncreaseAllowanceProperties.sol b/contracts/ERC20/external/properties/ERC20ExternalIncreaseAllowanceProperties.sol index abeae99..afea790 100644 --- a/contracts/ERC20/external/properties/ERC20ExternalIncreaseAllowanceProperties.sol +++ b/contracts/ERC20/external/properties/ERC20ExternalIncreaseAllowanceProperties.sol @@ -2,35 +2,57 @@ pragma solidity ^0.8.0; import "../util/ERC20ExternalTestBase.sol"; -abstract contract CryticERC20ExternalIncreaseAllowanceProperties is CryticERC20ExternalTestBase { - - constructor() { - - } +abstract contract CryticERC20ExternalIncreaseAllowanceProperties is + CryticERC20ExternalTestBase +{ + constructor() {} //////////////////////////////////////// // Properties // Allowance should be modified correctly via increase/decrease - function test_ERC20external_setAndIncreaseAllowance(address target, uint256 initialAmount, uint256 increaseAmount) public { + function test_ERC20external_setAndIncreaseAllowance( + address target, + uint256 initialAmount, + uint256 increaseAmount + ) public { bool r = token.approve(target, initialAmount); assertWithMsg(r == true, "Failed to set initial allowance via approve"); - assertEq(token.allowance(address(this), target), initialAmount, "Allowance not set correctly"); + assertEq( + token.allowance(address(this), target), + initialAmount, + "Allowance not set correctly" + ); r = token.increaseAllowance(target, increaseAmount); assertWithMsg(r == true, "Failed to increase allowance"); - assertEq(token.allowance(address(this), target), initialAmount+increaseAmount, "Allowance not increased correctly"); + assertEq( + token.allowance(address(this), target), + initialAmount + increaseAmount, + "Allowance not increased correctly" + ); } // Allowance should be modified correctly via increase/decrease - function test_ERC20external_setAndDecreaseAllowance(address target, uint256 initialAmount, uint256 decreaseAmount) public { + function test_ERC20external_setAndDecreaseAllowance( + address target, + uint256 initialAmount, + uint256 decreaseAmount + ) public { bool r = token.approve(target, initialAmount); assertWithMsg(r == true, "Failed to set initial allowance via approve"); - assertEq(token.allowance(address(this), target), initialAmount, "Allowance not set correctly"); + assertEq( + token.allowance(address(this), target), + initialAmount, + "Allowance not set correctly" + ); r = token.decreaseAllowance(target, decreaseAmount); assertWithMsg(r == true, "Failed to decrease allowance"); - assertEq(token.allowance(address(this), target), initialAmount-decreaseAmount, "Allowance not decreased correctly"); + assertEq( + token.allowance(address(this), target), + initialAmount - decreaseAmount, + "Allowance not decreased correctly" + ); } - } diff --git a/contracts/ERC20/external/properties/ERC20ExternalMintableProperties.sol b/contracts/ERC20/external/properties/ERC20ExternalMintableProperties.sol index 548b7a5..4c87b5a 100644 --- a/contracts/ERC20/external/properties/ERC20ExternalMintableProperties.sol +++ b/contracts/ERC20/external/properties/ERC20ExternalMintableProperties.sol @@ -2,22 +2,32 @@ pragma solidity ^0.8.0; import "../util/ERC20ExternalTestBase.sol"; -abstract contract CryticERC20ExternalMintableProperties is CryticERC20ExternalTestBase { - - constructor() { - - } +abstract contract CryticERC20ExternalMintableProperties is + CryticERC20ExternalTestBase +{ + constructor() {} //////////////////////////////////////// // Properties // Minting tokens should update user balance and total supply - function test_ERC20external_mintTokens(address target, uint256 amount) public { + function test_ERC20external_mintTokens( + address target, + uint256 amount + ) public { uint256 balance_receiver = token.balanceOf(target); uint256 supply = token.totalSupply(); token.mint(target, amount); - assertEq(token.balanceOf(target), balance_receiver+amount, "Mint failed to update target balance"); - assertEq(token.totalSupply(), supply+amount, "Mint failed to update total supply"); + assertEq( + token.balanceOf(target), + balance_receiver + amount, + "Mint failed to update target balance" + ); + assertEq( + token.totalSupply(), + supply + amount, + "Mint failed to update total supply" + ); } } diff --git a/contracts/ERC20/external/properties/ERC20ExternalPausableProperties.sol b/contracts/ERC20/external/properties/ERC20ExternalPausableProperties.sol index 86ecc72..8838060 100644 --- a/contracts/ERC20/external/properties/ERC20ExternalPausableProperties.sol +++ b/contracts/ERC20/external/properties/ERC20ExternalPausableProperties.sol @@ -3,52 +3,72 @@ pragma solidity ^0.8.0; import "../util/ERC20ExternalTestBase.sol"; import "../../../util/Hevm.sol"; -abstract contract CryticERC20ExternalPausableProperties is CryticERC20ExternalTestBase { - - constructor() { - - } +abstract contract CryticERC20ExternalPausableProperties is + CryticERC20ExternalTestBase +{ + constructor() {} //////////////////////////////////////// // Properties // Transfers should not be possible during paused state - function test_ERC20external_pausedTransfer(address target, uint256 amount) public { + function test_ERC20external_pausedTransfer( + address target, + uint256 amount + ) public { uint256 balance_sender = token.balanceOf(address(this)); uint256 balance_receiver = token.balanceOf(target); require(balance_sender > 0); - uint256 transfer_amount = amount % (balance_sender+1); + uint256 transfer_amount = amount % (balance_sender + 1); hevm.prank(token.owner()); token.pause(); bool r = token.transfer(target, transfer_amount); assertWithMsg(r == false, "Tokens transferred while paused"); - assertEq(token.balanceOf(address(this)), balance_sender, "Transfer while paused altered source balance"); - assertEq(token.balanceOf(target), balance_receiver, "Transfer while paused altered target balance"); + assertEq( + token.balanceOf(address(this)), + balance_sender, + "Transfer while paused altered source balance" + ); + assertEq( + token.balanceOf(target), + balance_receiver, + "Transfer while paused altered target balance" + ); hevm.prank(token.owner()); token.unpause(); } // Transfers should not be possible during paused state - function test_ERC20external_pausedTransferFrom(address target, uint256 amount) public { + function test_ERC20external_pausedTransferFrom( + address target, + uint256 amount + ) public { uint256 balance_sender = token.balanceOf(msg.sender); uint256 balance_receiver = token.balanceOf(target); uint256 allowance = token.allowance(msg.sender, address(this)); require(balance_sender > 0 && allowance > balance_sender); - uint256 transfer_amount = amount % (balance_sender+1); + uint256 transfer_amount = amount % (balance_sender + 1); hevm.prank(token.owner()); token.pause(); bool r = token.transferFrom(msg.sender, target, transfer_amount); assertWithMsg(r == false, "Tokens transferred while paused"); - assertEq(token.balanceOf(msg.sender), balance_sender, "Transfer while paused altered source balance"); - assertEq(token.balanceOf(target), balance_receiver, "Transfer while paused altered target balance"); + assertEq( + token.balanceOf(msg.sender), + balance_sender, + "Transfer while paused altered source balance" + ); + assertEq( + token.balanceOf(target), + balance_receiver, + "Transfer while paused altered target balance" + ); hevm.prank(token.owner()); token.unpause(); } - } diff --git a/contracts/ERC20/external/util/ERC20ExternalTestBase.sol b/contracts/ERC20/external/util/ERC20ExternalTestBase.sol index 86781fc..23a9526 100644 --- a/contracts/ERC20/external/util/ERC20ExternalTestBase.sol +++ b/contracts/ERC20/external/util/ERC20ExternalTestBase.sol @@ -4,11 +4,11 @@ import "../../../util/PropertiesHelper.sol"; import "./ITokenMock.sol"; import "../../../util/PropertiesConstants.sol"; -abstract contract CryticERC20ExternalTestBase is PropertiesAsserts, PropertiesConstants { - +abstract contract CryticERC20ExternalTestBase is + PropertiesAsserts, + PropertiesConstants +{ ITokenMock token; - constructor() { - } - + constructor() {} } diff --git a/contracts/ERC20/external/util/ITokenMock.sol b/contracts/ERC20/external/util/ITokenMock.sol index 39a24b0..d300494 100644 --- a/contracts/ERC20/external/util/ITokenMock.sol +++ b/contracts/ERC20/external/util/ITokenMock.sol @@ -4,15 +4,24 @@ import "../../../util/IERC20.sol"; interface ITokenMock is IERC20 { function isMintableOrBurnable() external returns (bool); + function initialSupply() external returns (uint256); function burn(uint256) external; + function burnFrom(address, uint256) external; + function mint(address, uint256) external; + function pause() external; + function unpause() external; + function paused() external returns (bool); + function owner() external returns (address); + function increaseAllowance(address, uint256) external returns (bool); + function decreaseAllowance(address, uint256) external returns (bool); -} \ No newline at end of file +} diff --git a/contracts/ERC20/internal/echidna.config.yaml b/contracts/ERC20/internal/echidna.config.yaml index b5ab450..44219c0 100644 --- a/contracts/ERC20/internal/echidna.config.yaml +++ b/contracts/ERC20/internal/echidna.config.yaml @@ -1,6 +1,6 @@ -coverage: true +coverage: true corpusDir: "corpus" testMode: assertion testLimit: 100000 deployer: "0x10000" -sender: ["0x10000", "0x20000", "0x30000"] \ No newline at end of file +sender: ["0x10000", "0x20000", "0x30000"] diff --git a/contracts/ERC20/internal/properties/ERC20BasicProperties.sol b/contracts/ERC20/internal/properties/ERC20BasicProperties.sol index ca85933..4159312 100644 --- a/contracts/ERC20/internal/properties/ERC20BasicProperties.sol +++ b/contracts/ERC20/internal/properties/ERC20BasicProperties.sol @@ -3,10 +3,7 @@ pragma solidity ^0.8.13; import "../util/ERC20TestBase.sol"; abstract contract CryticERC20BasicProperties is CryticERC20Base { - - constructor() { - - } + constructor() {} //////////////////////////////////////// // Properties @@ -19,18 +16,32 @@ abstract contract CryticERC20BasicProperties is CryticERC20Base { // User balance must not exceed total supply function test_ERC20_userBalanceNotHigherThanSupply() public { - assertLte(balanceOf(msg.sender), totalSupply(), "User balance higher than total supply"); + assertLte( + balanceOf(msg.sender), + totalSupply(), + "User balance higher than total supply" + ); } // Sum of users balance must not exceed total supply function test_ERC20_usersBalancesNotHigherThanSupply() public { - uint256 balance = balanceOf(USER1) + balanceOf(USER2) + balanceOf(USER3); - assertLte(balance, totalSupply(), "Sum of user balances higher than total supply"); + uint256 balance = balanceOf(USER1) + + balanceOf(USER2) + + balanceOf(USER3); + assertLte( + balance, + totalSupply(), + "Sum of user balances higher than total supply" + ); } // Address zero should have zero balance function test_ERC20_zeroAddressBalance() public { - assertEq(balanceOf(address(0)), 0, "Address zero balance not equal to zero"); + assertEq( + balanceOf(address(0)), + 0, + "Address zero balance not equal to zero" + ); } // Transfers to zero address should not be allowed @@ -47,7 +58,9 @@ abstract contract CryticERC20BasicProperties is CryticERC20Base { uint256 balance_sender = balanceOf(msg.sender); uint256 current_allowance = allowance(msg.sender, address(this)); require(balance_sender > 0 && current_allowance > 0); - uint256 maxValue = balance_sender >= current_allowance ? current_allowance : balance_sender; + uint256 maxValue = balance_sender >= current_allowance + ? current_allowance + : balance_sender; bool r = transferFrom(msg.sender, address(0), value % (maxValue + 1)); assertWithMsg(r == false, "Successful transferFrom to address zero"); @@ -58,11 +71,17 @@ abstract contract CryticERC20BasicProperties is CryticERC20Base { uint256 balance_sender = balanceOf(msg.sender); uint256 current_allowance = allowance(msg.sender, address(this)); require(balance_sender > 0 && current_allowance > 0); - uint256 maxValue = balance_sender >= current_allowance ? current_allowance : balance_sender; + uint256 maxValue = balance_sender >= current_allowance + ? current_allowance + : balance_sender; bool r = transferFrom(msg.sender, msg.sender, value % (maxValue + 1)); assertWithMsg(r == true, "Failed self transferFrom"); - assertEq(balance_sender, balanceOf(msg.sender), "Self transferFrom breaks accounting"); + assertEq( + balance_sender, + balanceOf(msg.sender), + "Self transferFrom breaks accounting" + ); } // Self transfers should not break accounting @@ -72,7 +91,11 @@ abstract contract CryticERC20BasicProperties is CryticERC20Base { bool r = this.transfer(address(this), value % (balance_sender + 1)); assertWithMsg(r == true, "Failed self transfer"); - assertEq(balance_sender, balanceOf(address(this)), "Self transfer breaks accounting"); + assertEq( + balance_sender, + balanceOf(address(this)), + "Self transfer breaks accounting" + ); } // Transfers for more than available balance should not be allowed @@ -81,11 +104,22 @@ abstract contract CryticERC20BasicProperties is CryticERC20Base { uint256 balance_receiver = balanceOf(target); uint256 current_allowance = allowance(msg.sender, address(this)); require(balance_sender > 0 && current_allowance > balance_sender); - - bool r = transferFrom(msg.sender, target, balance_sender+1); - assertWithMsg(r == false, "Successful transferFrom for more than account balance"); - assertEq(balanceOf(msg.sender), balance_sender, "TransferFrom for more than balance modified source balance"); - assertEq(balanceOf(target), balance_receiver, "TransferFrom for more than balance modified target balance"); + + bool r = transferFrom(msg.sender, target, balance_sender + 1); + assertWithMsg( + r == false, + "Successful transferFrom for more than account balance" + ); + assertEq( + balanceOf(msg.sender), + balance_sender, + "TransferFrom for more than balance modified source balance" + ); + assertEq( + balanceOf(target), + balance_receiver, + "TransferFrom for more than balance modified target balance" + ); } // Transfers for more than available balance should not be allowed @@ -94,10 +128,21 @@ abstract contract CryticERC20BasicProperties is CryticERC20Base { uint256 balance_receiver = balanceOf(target); require(balance_sender > 0); - bool r = this.transfer(target, balance_sender+1); - assertWithMsg(r == false, "Successful transfer for more than account balance"); - assertEq(balanceOf(address(this)), balance_sender, "Transfer for more than balance modified source balance"); - assertEq(balanceOf(target), balance_receiver, "Transfer for more than balance modified target balance"); + bool r = this.transfer(target, balance_sender + 1); + assertWithMsg( + r == false, + "Successful transfer for more than account balance" + ); + assertEq( + balanceOf(address(this)), + balance_sender, + "Transfer for more than balance modified source balance" + ); + assertEq( + balanceOf(target), + balance_receiver, + "Transfer for more than balance modified target balance" + ); } // Zero amount transfers should not break accounting @@ -108,8 +153,16 @@ abstract contract CryticERC20BasicProperties is CryticERC20Base { bool r = transfer(target, 0); assertWithMsg(r == true, "Zero amount transfer failed"); - assertEq(balanceOf(address(this)), balance_sender, "Zero amount transfer modified source balance"); - assertEq(balanceOf(target), balance_receiver, "Zero amount transfer modified target balance"); + assertEq( + balanceOf(address(this)), + balance_sender, + "Zero amount transfer modified source balance" + ); + assertEq( + balanceOf(target), + balance_receiver, + "Zero amount transfer modified target balance" + ); } // Zero amount transfers should not break accounting @@ -121,8 +174,16 @@ abstract contract CryticERC20BasicProperties is CryticERC20Base { bool r = transferFrom(msg.sender, target, 0); assertWithMsg(r == true, "Zero amount transferFrom failed"); - assertEq(balanceOf(msg.sender), balance_sender, "Zero amount transferFrom modified source balance"); - assertEq(balanceOf(target), balance_receiver, "Zero amount transferFrom modified target balance"); + assertEq( + balanceOf(msg.sender), + balance_sender, + "Zero amount transferFrom modified source balance" + ); + assertEq( + balanceOf(target), + balance_receiver, + "Zero amount transferFrom modified target balance" + ); } // Transfers should update accounting correctly @@ -135,8 +196,16 @@ abstract contract CryticERC20BasicProperties is CryticERC20Base { bool r = this.transfer(target, transfer_value); assertWithMsg(r == true, "transfer failed"); - assertEq(balanceOf(address(this)), balance_sender-transfer_value, "Wrong source balance after transfer"); - assertEq(balanceOf(target), balance_receiver+transfer_value, "Wrong target balance after transfer"); + assertEq( + balanceOf(address(this)), + balance_sender - transfer_value, + "Wrong source balance after transfer" + ); + assertEq( + balanceOf(target), + balance_receiver + transfer_value, + "Wrong target balance after transfer" + ); } // Transfers should update accounting correctly @@ -151,30 +220,56 @@ abstract contract CryticERC20BasicProperties is CryticERC20Base { bool r = transferFrom(msg.sender, target, transfer_value); assertWithMsg(r == true, "transferFrom failed"); - assertEq(balanceOf(msg.sender), balance_sender - transfer_value, "Wrong source balance after transferFrom"); - assertEq(balanceOf(target), balance_receiver + transfer_value, "Wrong target balance after transferFrom"); + assertEq( + balanceOf(msg.sender), + balance_sender - transfer_value, + "Wrong source balance after transferFrom" + ); + assertEq( + balanceOf(target), + balance_receiver + transfer_value, + "Wrong target balance after transferFrom" + ); } // Approve should set correct allowances function test_ERC20_setAllowance(address target, uint256 amount) public { bool r = this.approve(target, amount); assertWithMsg(r == true, "Failed to set allowance via approve"); - assertEq(allowance(address(this), target), amount, "Allowance not set correctly"); + assertEq( + allowance(address(this), target), + amount, + "Allowance not set correctly" + ); } // Approve should set correct allowances - function test_ERC20_setAllowanceTwice(address target, uint256 amount) public { + function test_ERC20_setAllowanceTwice( + address target, + uint256 amount + ) public { bool r = this.approve(target, amount); assertWithMsg(r == true, "Failed to set allowance via approve"); - assertEq(allowance(address(this), target), amount, "Allowance not set correctly"); + assertEq( + allowance(address(this), target), + amount, + "Allowance not set correctly" + ); - r = this.approve(target, amount/2); + r = this.approve(target, amount / 2); assertWithMsg(r == true, "Failed to set allowance via approve"); - assertEq(allowance(address(this), target), amount/2, "Allowance not set correctly"); + assertEq( + allowance(address(this), target), + amount / 2, + "Allowance not set correctly" + ); } // TransferFrom should decrease allowance - function test_ERC20_spendAllowanceAfterTransfer(address target, uint256 amount) public { + function test_ERC20_spendAllowanceAfterTransfer( + address target, + uint256 amount + ) public { require(target != address(this) && target != address(0)); require(target != msg.sender); uint256 balance_sender = balanceOf(msg.sender); @@ -184,11 +279,14 @@ abstract contract CryticERC20BasicProperties is CryticERC20Base { bool r = this.transferFrom(msg.sender, target, transfer_value); assertWithMsg(r == true, "transferFrom failed"); - + // Some implementations take an allowance of 2**256-1 as infinite, and therefore don't update if (current_allowance != type(uint256).max) { - assertEq(allowance(msg.sender, address(this)), current_allowance - transfer_value, "Allowance not updated correctly"); + assertEq( + allowance(msg.sender, address(this)), + current_allowance - transfer_value, + "Allowance not updated correctly" + ); } } - } diff --git a/contracts/ERC20/internal/properties/ERC20BurnableProperties.sol b/contracts/ERC20/internal/properties/ERC20BurnableProperties.sol index 9b74eea..d984e45 100644 --- a/contracts/ERC20/internal/properties/ERC20BurnableProperties.sol +++ b/contracts/ERC20/internal/properties/ERC20BurnableProperties.sol @@ -3,8 +3,10 @@ pragma solidity ^0.8.13; import "../util/ERC20TestBase.sol"; import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol"; -abstract contract CryticERC20BurnableProperties is CryticERC20Base, ERC20Burnable { - +abstract contract CryticERC20BurnableProperties is + CryticERC20Base, + ERC20Burnable +{ constructor() { isMintableOrBurnable = true; } @@ -17,11 +19,19 @@ abstract contract CryticERC20BurnableProperties is CryticERC20Base, ERC20Burnabl uint256 balance_sender = balanceOf(address(this)); uint256 supply = totalSupply(); require(balance_sender > 0); - uint256 burn_amount = amount % (balance_sender+1); + uint256 burn_amount = amount % (balance_sender + 1); this.burn(burn_amount); - assertEq(balanceOf(address(this)), balance_sender - burn_amount, "Source balance incorrect after burn"); - assertEq(totalSupply(), supply-burn_amount, "Total supply incorrect after burn"); + assertEq( + balanceOf(address(this)), + balance_sender - burn_amount, + "Source balance incorrect after burn" + ); + assertEq( + totalSupply(), + supply - burn_amount, + "Total supply incorrect after burn" + ); } // Burn should update user balance and total supply @@ -30,26 +40,37 @@ abstract contract CryticERC20BurnableProperties is CryticERC20Base, ERC20Burnabl uint256 allowance = allowance(msg.sender, address(this)); require(balance_sender > 0 && allowance > balance_sender); uint256 supply = totalSupply(); - uint256 burn_amount = amount % (balance_sender+1); + uint256 burn_amount = amount % (balance_sender + 1); this.burnFrom(msg.sender, burn_amount); - assertEq(balanceOf(msg.sender), balance_sender - burn_amount, "Source balance incorrect after burnFrom"); - assertEq(totalSupply(), supply-burn_amount, "Total supply incorrect after burnFrom"); + assertEq( + balanceOf(msg.sender), + balance_sender - burn_amount, + "Source balance incorrect after burnFrom" + ); + assertEq( + totalSupply(), + supply - burn_amount, + "Total supply incorrect after burnFrom" + ); } // burnFrom should update allowance - function test_ERC20_burnFromUpdateAllowance(uint256 amount) public { + function test_ERC20_burnFromUpdateAllowance(uint256 amount) public { uint256 balance_sender = balanceOf(msg.sender); uint256 current_allowance = allowance(msg.sender, address(this)); require(balance_sender > 0 && current_allowance > balance_sender); - uint256 burn_amount = amount % (balance_sender+1); + uint256 burn_amount = amount % (balance_sender + 1); this.burnFrom(msg.sender, burn_amount); // Some implementations take an allowance of 2**256-1 as infinite, and therefore don't update if (current_allowance != type(uint256).max) { - assertEq(allowance(msg.sender, address(this)), current_allowance - burn_amount, "Allowance not updated correctly"); + assertEq( + allowance(msg.sender, address(this)), + current_allowance - burn_amount, + "Allowance not updated correctly" + ); } } - } diff --git a/contracts/ERC20/internal/properties/ERC20IncreaseAllowanceProperties.sol b/contracts/ERC20/internal/properties/ERC20IncreaseAllowanceProperties.sol index 910d769..52d8fc8 100644 --- a/contracts/ERC20/internal/properties/ERC20IncreaseAllowanceProperties.sol +++ b/contracts/ERC20/internal/properties/ERC20IncreaseAllowanceProperties.sol @@ -4,34 +4,54 @@ import "../util/ERC20TestBase.sol"; import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol"; abstract contract CryticERC20IncreaseAllowanceProperties is CryticERC20Base { - - constructor() { - - } + constructor() {} //////////////////////////////////////// // Properties // Allowance should be modified correctly via increase/decrease - function test_ERC20_setAndIncreaseAllowance(address target, uint256 initialAmount, uint256 increaseAmount) public { + function test_ERC20_setAndIncreaseAllowance( + address target, + uint256 initialAmount, + uint256 increaseAmount + ) public { bool r = this.approve(target, initialAmount); assertWithMsg(r == true, "Failed to set initial allowance via approve"); - assertEq(allowance(address(this), target), initialAmount, "Allowance not set correctly"); + assertEq( + allowance(address(this), target), + initialAmount, + "Allowance not set correctly" + ); r = this.increaseAllowance(target, increaseAmount); assertWithMsg(r == true, "Failed to increase allowance"); - assertEq(allowance(address(this), target), initialAmount+increaseAmount, "Allowance not increased correctly"); + assertEq( + allowance(address(this), target), + initialAmount + increaseAmount, + "Allowance not increased correctly" + ); } // Allowance should be modified correctly via increase/decrease - function test_ERC20_setAndDecreaseAllowance(address target, uint256 initialAmount, uint256 decreaseAmount) public { + function test_ERC20_setAndDecreaseAllowance( + address target, + uint256 initialAmount, + uint256 decreaseAmount + ) public { bool r = this.approve(target, initialAmount); assertWithMsg(r == true, "Failed to set initial allowance via approve"); - assertEq(allowance(address(this), target), initialAmount, "Allowance not set correctly"); + assertEq( + allowance(address(this), target), + initialAmount, + "Allowance not set correctly" + ); r = this.decreaseAllowance(target, decreaseAmount); assertWithMsg(r == true, "Failed to decrease allowance"); - assertEq(allowance(address(this), target), initialAmount-decreaseAmount, "Allowance not decreased correctly"); + assertEq( + allowance(address(this), target), + initialAmount - decreaseAmount, + "Allowance not decreased correctly" + ); } - } diff --git a/contracts/ERC20/internal/properties/ERC20MintableProperties.sol b/contracts/ERC20/internal/properties/ERC20MintableProperties.sol index cac5f77..fabfb51 100644 --- a/contracts/ERC20/internal/properties/ERC20MintableProperties.sol +++ b/contracts/ERC20/internal/properties/ERC20MintableProperties.sol @@ -3,7 +3,6 @@ pragma solidity ^0.8.13; import "../util/ERC20TestBase.sol"; abstract contract CryticERC20MintableProperties is CryticERC20Base { - constructor() { isMintableOrBurnable = true; } @@ -20,7 +19,15 @@ abstract contract CryticERC20MintableProperties is CryticERC20Base { uint256 supply = totalSupply(); this.mint(target, amount); - assertEq(balanceOf(target), balance_receiver+amount, "Mint failed to update target balance"); - assertEq(totalSupply(), supply+amount, "Mint failed to update total supply"); + assertEq( + balanceOf(target), + balance_receiver + amount, + "Mint failed to update target balance" + ); + assertEq( + totalSupply(), + supply + amount, + "Mint failed to update total supply" + ); } } diff --git a/contracts/ERC20/internal/properties/ERC20PausableProperties.sol b/contracts/ERC20/internal/properties/ERC20PausableProperties.sol index a2a560b..35edae7 100644 --- a/contracts/ERC20/internal/properties/ERC20PausableProperties.sol +++ b/contracts/ERC20/internal/properties/ERC20PausableProperties.sol @@ -3,15 +3,17 @@ pragma solidity ^0.8.13; import "../util/ERC20TestBase.sol"; import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Pausable.sol"; -abstract contract CryticERC20PausableProperties is CryticERC20Base, ERC20Pausable { - +abstract contract CryticERC20PausableProperties is + CryticERC20Base, + ERC20Pausable +{ constructor() {} //////////////////////////////////////// // Helper functions - May need tweaking for non-OZ tokens function _overridePause(bool paused) internal { - if(paused) { + if (paused) { _pause(); } else { _unpause(); @@ -35,34 +37,52 @@ abstract contract CryticERC20PausableProperties is CryticERC20Base, ERC20Pausabl uint256 balance_sender = balanceOf(address(this)); uint256 balance_receiver = balanceOf(target); require(balance_sender > 0); - uint256 transfer_amount = amount % (balance_sender+1); + uint256 transfer_amount = amount % (balance_sender + 1); _pause(); bool r = this.transfer(target, transfer_amount); assertWithMsg(r == false, "Tokens transferred while paused"); - assertEq(balanceOf(address(this)), balance_sender, "Transfer while paused altered source balance"); - assertEq(balanceOf(target), balance_receiver, "Transfer while paused altered target balance"); + assertEq( + balanceOf(address(this)), + balance_sender, + "Transfer while paused altered source balance" + ); + assertEq( + balanceOf(target), + balance_receiver, + "Transfer while paused altered target balance" + ); _unpause(); } // Transfers should not be possible during paused state - function test_ERC20_pausedTransferFrom(address target, uint256 amount) public { + function test_ERC20_pausedTransferFrom( + address target, + uint256 amount + ) public { uint256 balance_sender = balanceOf(msg.sender); uint256 balance_receiver = balanceOf(target); uint256 allowance = allowance(msg.sender, address(this)); require(balance_sender > 0 && allowance > balance_sender); - uint256 transfer_amount = amount % (balance_sender+1); + uint256 transfer_amount = amount % (balance_sender + 1); _pause(); bool r = this.transferFrom(msg.sender, target, transfer_amount); assertWithMsg(r == false, "Tokens transferred while paused"); - assertEq(balanceOf(msg.sender), balance_sender, "Transfer while paused altered source balance"); - assertEq(balanceOf(target), balance_receiver, "Transfer while paused altered target balance"); + assertEq( + balanceOf(msg.sender), + balance_sender, + "Transfer while paused altered source balance" + ); + assertEq( + balanceOf(target), + balance_receiver, + "Transfer while paused altered target balance" + ); _unpause(); } - } diff --git a/contracts/ERC20/internal/util/ERC20TestBase.sol b/contracts/ERC20/internal/util/ERC20TestBase.sol index 5b7de94..1f4925f 100644 --- a/contracts/ERC20/internal/util/ERC20TestBase.sol +++ b/contracts/ERC20/internal/util/ERC20TestBase.sol @@ -4,15 +4,16 @@ import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "../../../util/PropertiesConstants.sol"; import "../../../util/PropertiesHelper.sol"; -abstract contract CryticERC20Base is ERC20, PropertiesAsserts, PropertiesConstants { - +abstract contract CryticERC20Base is + ERC20, + PropertiesAsserts, + PropertiesConstants +{ // Initial supply after deploying uint256 initialSupply; // Is the contract allowed to change its total supply? bool isMintableOrBurnable; - constructor() { - } - + constructor() {} } diff --git a/contracts/ERC4626/ERC4626PropertyTests.sol b/contracts/ERC4626/ERC4626PropertyTests.sol index 5540f4f..83f83df 100644 --- a/contracts/ERC4626/ERC4626PropertyTests.sol +++ b/contracts/ERC4626/ERC4626PropertyTests.sol @@ -11,11 +11,13 @@ import {CryticERC4626VaultProxy} from "./properties/VaultProxy.sol"; import {CryticERC4626SecurityProps} from "./properties/SecurityProps.sol"; /// @notice Aggregator contract for various 4626 property tests. Inherit from this & echidna will test all properties at the same time. -contract CryticERC4626PropertyTests is - CryticERC4626RedeemUsingApproval, - CryticERC4626MustNotRevert, +contract CryticERC4626PropertyTests is + CryticERC4626RedeemUsingApproval, + CryticERC4626MustNotRevert, CryticERC4626SenderIndependent, CryticERC4626FunctionalAccounting, - CryticERC4626Rounding, - CryticERC4626SecurityProps{ -} \ No newline at end of file + CryticERC4626Rounding, + CryticERC4626SecurityProps +{ + +} diff --git a/contracts/ERC4626/README.md b/contracts/ERC4626/README.md index 3410a99..06d2ce2 100644 --- a/contracts/ERC4626/README.md +++ b/contracts/ERC4626/README.md @@ -1,4 +1,3 @@ - # ERC4626 Echidna Property Tests - [ERC4626 Echidna Property Tests](#erc4626-echidna-property-tests) @@ -19,10 +18,12 @@ ## Consuming ### Initial setup + To use these properties to test a given vault implementation, see the readme in the project root. ### Adding internal test methods to a vault -Some properties of the ERC4626 spec cannot be tested externally because testing them requires interactions between the test suite & functionality that is not defined in the spec. + +Some properties of the ERC4626 spec cannot be tested externally because testing them requires interactions between the test suite & functionality that is not defined in the spec. To compensate for this limitation, a vault under test may optionally implement a set of methods that allow such properties to be tested. See [IERC4626Internal](util/IERC4626Internal.sol) for the list of methods. @@ -51,32 +52,38 @@ Running tests(used to validate the properties are working correctly): `echidna-test ./contracts/ERC4626/test/rounding/BadConvertToAssetsRounding.sol --contract TestHarness --config ./contracts/ERC4626/test/echidna.config.yaml` Should cause these properties to fail: + - verify_previewRedeemRoundingDirection - verify_redeemRoundingDirection - verify_convertToAssetsRoundingDirection `echidna-test ./contracts/ERC4626/test/rounding/BadConvertToSharesRounding.sol --contract TestHarness --config ./contracts/ERC4626/test/echidna.config.yaml` Should cause these properties to fail: + - verify_convertToSharesRoundingDirection - verify_previewDepositRoundingDirection - verify_depositRoundingDirection `echidna-test ./contracts/ERC4626/test/rounding/BadPreviewMintRounding.sol --contract TestHarness --config ./contracts/ERC4626/test/echidna.config.yaml` Should cause these properties to fail: + - verify_previewMintRoundingDirection - verify_mintRoundingDirection `echidna-test ./contracts/ERC4626/test/rounding/BadPreviewWithdrawRounding.sol --contract TestHarness --config ./contracts/ERC4626/test/echidna.config.yaml` Should cause these properties to fail: + - verify_previewWithdrawRoundingDirection - verify_withdrawRoundingDirection `echidna-test ./contracts/ERC4626/test/security/BadShareInflation.sol --contract TestHarness --config ./contracts/ERC4626/test/echidna.config.yaml` Should cause these properties to fail: + - verify_sharePriceInflationAttack `echidna-test ./contracts/ERC4626/test/usingApproval/BadAllowanceUpdate.sol --contract TestHarness --config ./contracts/ERC4626/test/echidna.config.yaml` Should cause these properties to fail: + - verify_redeemViaApprovalProxy - verify_withdrawViaApprovalProxy - verify_redeemRequiresTokenApproval @@ -91,6 +98,7 @@ Run property tests against vanilla solmate: ## Properties Tested ### MustNotRevertProps + - `convertToAssets()` must not revert for reasonable values - `convertToShares()` must not revert for reasonable values - `asset()` must not revert @@ -101,6 +109,7 @@ Run property tests against vanilla solmate: - `maxWithdraw()` must not revert ### FunctionalAccountingProps + - `deposit()` must deduct assets from the owner - `deposit()` must credit shares to the receiver - `deposit()` must mint greater than or equal to the number of shares predicted by `previewDeposit()` @@ -115,14 +124,16 @@ Run property tests against vanilla solmate: - `redeem()` must credit greater than or equal to the number of assets predicted by `previewRedeem()` ### RedeemUsingApprovalProps + - `withdraw()` must allow proxies to withdraw tokens on behalf of the owner using share token approvals - `redeem()` must allow proxies to redeem shares on behalf of the owner using share token approvals -- Third party `withdraw()` calls must update the msg.sender's allowance -- Third party `redeem()` calls must update the msg.sender's allowance -- Third parties must not be able to `withdraw()` tokens on an owner's behalf without a token approval -- Third parties must not be able to `redeem()` shares on an owner's behalf without a token approval - +- Third party `withdraw()` calls must update the msg.sender's allowance +- Third party `redeem()` calls must update the msg.sender's allowance +- Third parties must not be able to `withdraw()` tokens on an owner's behalf without a token approval +- Third parties must not be able to `redeem()` shares on an owner's behalf without a token approval + ### SenderIndependentProps + - `maxDeposit()` must assume the receiver/sender has infinite assets - `maxMint()` must assume the receiver/sender has infinite assets - `previewMint()` must not account for msg.sender asset balance @@ -131,6 +142,7 @@ Run property tests against vanilla solmate: - `previewRedeem()` must not account for msg.sender share balance ### RoundingProps + - Shares may never be minted for free using: - `previewDeposit()` - `previewMint()` @@ -138,19 +150,21 @@ Run property tests against vanilla solmate: - Tokens may never be withdrawn for free using: - `previewWithdraw()` - `previewRedeem()` - - `convertToAssets()` + - `convertToAssets()` - Shares may never be minted for free using: - `deposit()` - - `mint()` + - `mint()` - Tokens may never be withdrawn for free using: - `withdraw()` - `redeem()` ### SecurityProps -- `decimals()` should be larger than or equal to `asset.decimals()` -- Accounting system must not be vulnerable to share price inflation attacks + +- `decimals()` should be larger than or equal to `asset.decimals()` +- Accounting system must not be vulnerable to share price inflation attacks ## Properties to consider adding + - deposit/mint must increase totalSupply/totalAssets - withdraw/redeem must decrease totalSupply/totalAssets - `previewDeposit()` must not account for vault specific/user/global limits @@ -159,5 +173,6 @@ Run property tests against vanilla solmate: - `previewRedeem()` must not account for vault specific/user/global limits ## Properties that may not be testable + - Note that any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in share price or some other type of condition, meaning the depositor will lose assets by depositing. -- Whether a given method is inclusive of withdraw/deposit fees \ No newline at end of file +- Whether a given method is inclusive of withdraw/deposit fees diff --git a/contracts/ERC4626/properties/FunctionalAccountingProps.sol b/contracts/ERC4626/properties/FunctionalAccountingProps.sol index 40a7c50..56e2381 100644 --- a/contracts/ERC4626/properties/FunctionalAccountingProps.sol +++ b/contracts/ERC4626/properties/FunctionalAccountingProps.sol @@ -7,26 +7,59 @@ contract CryticERC4626FunctionalAccounting is CryticERC4626PropertyBase { /// - deposit() must deduct assets from the owner /// - deposit() must credit shares to the receiver /// - deposit() must mint greater than or equal to the number of shares predicted by previewDeposit() - function verify_depositProperties(uint256 receiverId, uint256 tokens) public { + function verify_depositProperties( + uint256 receiverId, + uint256 tokens + ) public { address sender = address(this); address receiver = restrictAddressToThirdParties(receiverId); tokens = requireValidDepositAmount(sender, receiver, tokens); - (uint256 senderAssetsBeforeDeposit,) = measureAddressHoldings(sender, "sender", "before deposit"); - (, uint256 receiverSharesBeforeDeposit) = measureAddressHoldings(receiver, "receiver", "before deposit"); + (uint256 senderAssetsBeforeDeposit, ) = measureAddressHoldings( + sender, + "sender", + "before deposit" + ); + (, uint256 receiverSharesBeforeDeposit) = measureAddressHoldings( + receiver, + "receiver", + "before deposit" + ); uint256 sharesExpected = vault.previewDeposit(tokens); uint256 sharesMinted = vault.deposit(tokens, receiver); - assertGte(sharesMinted, sharesExpected, "deposit() must always mint greater than or equal to the shares predicted by previewDeposit()"); - - (uint256 senderAssetsAfterDeposit,) = measureAddressHoldings(sender, "sender", "after deposit"); - (, uint256 receiverSharesAfterDeposit) = measureAddressHoldings(receiver, "receiver", "after deposit"); - - uint256 senderAssetsDelta = senderAssetsBeforeDeposit - senderAssetsAfterDeposit; - assertEq(senderAssetsDelta, tokens, "deposit() must consume exactly the number of tokens requested"); - - uint256 receiverSharesDelta = receiverSharesAfterDeposit - receiverSharesBeforeDeposit; - assertEq(receiverSharesDelta, sharesMinted, "deposit() must credit the correct number of shares to the receiver"); + assertGte( + sharesMinted, + sharesExpected, + "deposit() must always mint greater than or equal to the shares predicted by previewDeposit()" + ); + + (uint256 senderAssetsAfterDeposit, ) = measureAddressHoldings( + sender, + "sender", + "after deposit" + ); + (, uint256 receiverSharesAfterDeposit) = measureAddressHoldings( + receiver, + "receiver", + "after deposit" + ); + + uint256 senderAssetsDelta = senderAssetsBeforeDeposit - + senderAssetsAfterDeposit; + assertEq( + senderAssetsDelta, + tokens, + "deposit() must consume exactly the number of tokens requested" + ); + + uint256 receiverSharesDelta = receiverSharesAfterDeposit - + receiverSharesBeforeDeposit; + assertEq( + receiverSharesDelta, + sharesMinted, + "deposit() must credit the correct number of shares to the receiver" + ); } /// @notice Validates the following properties: @@ -39,73 +72,167 @@ contract CryticERC4626FunctionalAccounting is CryticERC4626PropertyBase { uint256 tokensExpected = vault.previewMint(shares); shares = requireValidMintAmount(sender, receiver, shares); - (uint256 senderAssetsBeforeMint,) = measureAddressHoldings(sender, "sender", "before mint"); - (, uint256 receiverSharesBeforeMint) = measureAddressHoldings(receiver, "receiver", "before mint"); + (uint256 senderAssetsBeforeMint, ) = measureAddressHoldings( + sender, + "sender", + "before mint" + ); + (, uint256 receiverSharesBeforeMint) = measureAddressHoldings( + receiver, + "receiver", + "before mint" + ); uint256 tokensConsumed = vault.mint(shares, receiver); - assertLte(tokensConsumed, tokensExpected, "mint() must always consume less than or equal to the tokens predicted by previewMint()"); - - (uint256 senderAssetsAfterMint,) = measureAddressHoldings(sender, "sender", "after mint"); - (, uint256 receiverSharesAfterMint) = measureAddressHoldings(receiver, "receiver", "after mint"); - - uint256 senderAssetsDelta = senderAssetsBeforeMint - senderAssetsAfterMint; - assertEq(senderAssetsDelta, tokensConsumed, "mint() must consume exactly the number of tokens requested"); - - uint256 receiverSharesDelta = receiverSharesAfterMint - receiverSharesBeforeMint; - assertEq(receiverSharesDelta, shares, "mint() must credit the correct number of shares to the receiver"); + assertLte( + tokensConsumed, + tokensExpected, + "mint() must always consume less than or equal to the tokens predicted by previewMint()" + ); + + (uint256 senderAssetsAfterMint, ) = measureAddressHoldings( + sender, + "sender", + "after mint" + ); + (, uint256 receiverSharesAfterMint) = measureAddressHoldings( + receiver, + "receiver", + "after mint" + ); + + uint256 senderAssetsDelta = senderAssetsBeforeMint - + senderAssetsAfterMint; + assertEq( + senderAssetsDelta, + tokensConsumed, + "mint() must consume exactly the number of tokens requested" + ); + + uint256 receiverSharesDelta = receiverSharesAfterMint - + receiverSharesBeforeMint; + assertEq( + receiverSharesDelta, + shares, + "mint() must credit the correct number of shares to the receiver" + ); } /// @notice Validates the following properties: /// - redeem() must deduct shares from the owner /// - redeem() must credit assets to the receiver /// - redeem() must credit greater than or equal to the number of assets predicted by previewRedeem() - function verify_redeemProperties(uint256 receiverId, uint256 shares) public { + function verify_redeemProperties( + uint256 receiverId, + uint256 shares + ) public { // we can only redeem on behalf of address(this) until we get cheatcodes address owner = address(this); address receiver = restrictAddressToThirdParties(receiverId); shares = requireValidRedeemAmount(owner, shares); - (, uint256 ownerSharesBefore) = measureAddressHoldings(owner, "owner", "before redeem"); - (uint256 receiverAssetsBefore,) = measureAddressHoldings(receiver, "receiver", "before redeem"); + (, uint256 ownerSharesBefore) = measureAddressHoldings( + owner, + "owner", + "before redeem" + ); + (uint256 receiverAssetsBefore, ) = measureAddressHoldings( + receiver, + "receiver", + "before redeem" + ); uint256 tokensExpected = vault.previewRedeem(shares); uint256 tokensWithdrawn = vault.redeem(shares, receiver, owner); - assertGte(tokensWithdrawn, tokensExpected, "redeem() must withdraw greater than or equal to the number of assets predicted by previewRedeem()"); - - (, uint256 ownerSharesAfter) = measureAddressHoldings(owner, "owner", "after redeem"); - (uint256 receiverAssetsAfter,) = measureAddressHoldings(receiver, "receiver", "after redeem"); - - uint256 receiverAssetsDelta = receiverAssetsAfter - receiverAssetsBefore; - assertEq(receiverAssetsDelta, tokensWithdrawn, "redeem() must credit the correct number of assets to the receiver"); + assertGte( + tokensWithdrawn, + tokensExpected, + "redeem() must withdraw greater than or equal to the number of assets predicted by previewRedeem()" + ); + + (, uint256 ownerSharesAfter) = measureAddressHoldings( + owner, + "owner", + "after redeem" + ); + (uint256 receiverAssetsAfter, ) = measureAddressHoldings( + receiver, + "receiver", + "after redeem" + ); + + uint256 receiverAssetsDelta = receiverAssetsAfter - + receiverAssetsBefore; + assertEq( + receiverAssetsDelta, + tokensWithdrawn, + "redeem() must credit the correct number of assets to the receiver" + ); uint256 ownerSharesDelta = ownerSharesBefore - ownerSharesAfter; - assertEq(ownerSharesDelta, shares, "redeem() must deduct the correct number of shares from the owner"); + assertEq( + ownerSharesDelta, + shares, + "redeem() must deduct the correct number of shares from the owner" + ); } /// @notice Validates the following properties: /// - withdraw() must deduct shares from the owner /// - withdraw() must credit assets to the receiver /// - withdraw() must deduct less than or equal to the number of shares predicted by previewWithdraw() - function verify_withdrawProperties(uint256 receiverId, uint256 tokens) public { + function verify_withdrawProperties( + uint256 receiverId, + uint256 tokens + ) public { // we can only withdraw on behalf of address(this) until we get cheatcodes address owner = address(this); address receiver = restrictAddressToThirdParties(receiverId); tokens = requireValidWithdrawAmount(owner, tokens); uint256 sharesExpected = vault.previewWithdraw(tokens); - (, uint256 ownerSharesBefore) = measureAddressHoldings(owner, "owner", "before withdraw"); - (uint256 receiverAssetsBefore,) = measureAddressHoldings(receiver, "receiver", "before withdraw"); + (, uint256 ownerSharesBefore) = measureAddressHoldings( + owner, + "owner", + "before withdraw" + ); + (uint256 receiverAssetsBefore, ) = measureAddressHoldings( + receiver, + "receiver", + "before withdraw" + ); uint256 sharesRedeemed = vault.withdraw(tokens, receiver, owner); - assertLte(sharesRedeemed, sharesExpected, "withdraw() must redeem less than or equal to the number of shares predicted by previewWithdraw()"); - - (, uint256 ownerSharesAfter) = measureAddressHoldings(owner, "owner", "after withdraw"); - (uint256 receiverAssetsAfter,) = measureAddressHoldings(receiver, "receiver", "after withdraw"); - - uint256 receiverAssetsDelta = receiverAssetsAfter - receiverAssetsBefore; - assertEq(receiverAssetsDelta, tokens, "withdraw() must credit the correct number of assets to the receiver"); + assertLte( + sharesRedeemed, + sharesExpected, + "withdraw() must redeem less than or equal to the number of shares predicted by previewWithdraw()" + ); + + (, uint256 ownerSharesAfter) = measureAddressHoldings( + owner, + "owner", + "after withdraw" + ); + (uint256 receiverAssetsAfter, ) = measureAddressHoldings( + receiver, + "receiver", + "after withdraw" + ); + + uint256 receiverAssetsDelta = receiverAssetsAfter - + receiverAssetsBefore; + assertEq( + receiverAssetsDelta, + tokens, + "withdraw() must credit the correct number of assets to the receiver" + ); uint256 ownerSharesDelta = ownerSharesBefore - ownerSharesAfter; - assertEq(ownerSharesDelta, sharesRedeemed, "withdraw() must deduct the correct number of shares from the owner"); + assertEq( + ownerSharesDelta, + sharesRedeemed, + "withdraw() must deduct the correct number of shares from the owner" + ); } } diff --git a/contracts/ERC4626/properties/MustNotRevertProps.sol b/contracts/ERC4626/properties/MustNotRevertProps.sol index 59087e5..0b542c4 100644 --- a/contracts/ERC4626/properties/MustNotRevertProps.sol +++ b/contracts/ERC4626/properties/MustNotRevertProps.sol @@ -2,8 +2,11 @@ pragma solidity ^0.8.0; import {CryticERC4626PropertyBase} from "../util/ERC4626PropertyTestBase.sol"; import {CryticERC4626VaultProxy} from "./VaultProxy.sol"; -contract CryticERC4626MustNotRevert is CryticERC4626PropertyBase, CryticERC4626VaultProxy { +contract CryticERC4626MustNotRevert is + CryticERC4626PropertyBase, + CryticERC4626VaultProxy +{ /// @notice Validates the following properties: /// - vault.asset() must not revert function verify_assetMustNotRevert() public { @@ -28,18 +31,17 @@ contract CryticERC4626MustNotRevert is CryticERC4626PropertyBase, CryticERC4626V /// - vault.convertToAssets() must not revert for reasonable values function verify_convertToAssetsMustNotRevert(uint256 shares) public { // arbitrarily define "reasonable values" to be 10**(token.decimals+20) - uint256 reasonably_largest_value = 10**(vault.decimals()+20); + uint256 reasonably_largest_value = 10 ** (vault.decimals() + 20); // prevent scenarios where there is enough totalSupply to trigger overflows require(vault.totalSupply() <= reasonably_largest_value); shares = clampLte(shares, reasonably_largest_value); - + // exclude the possibility of idiosyncratic reverts. Might have to add more in future. shares = clampLte(shares, vault.totalSupply()); emit LogUint256("totalSupply", vault.totalSupply()); emit LogUint256("totalAssets", vault.totalAssets()); - try vault.convertToAssets(shares) { return; @@ -52,7 +54,7 @@ contract CryticERC4626MustNotRevert is CryticERC4626PropertyBase, CryticERC4626V /// - vault.convertToShares() must not revert for reasonable values function verify_convertToSharesMustNotRevert(uint256 tokens) public { // arbitrarily define "reasonable values" to be 10**(token.decimals+20) - uint256 reasonably_largest_value = 10**(asset.decimals()+20); + uint256 reasonably_largest_value = 10 ** (asset.decimals() + 20); // prevent scenarios where there is enough totalSupply to trigger overflows require(asset.totalSupply() <= reasonably_largest_value); @@ -99,7 +101,7 @@ contract CryticERC4626MustNotRevert is CryticERC4626PropertyBase, CryticERC4626V // if the following reverts from overflow, bail out. // additional criterion might be required in the future vault.convertToAssets(vault.balanceOf(owner)); - + try vault.maxWithdraw(owner) { return; } catch { diff --git a/contracts/ERC4626/properties/RedeemUsingApprovalProps.sol b/contracts/ERC4626/properties/RedeemUsingApprovalProps.sol index e81826e..8f1fe0e 100644 --- a/contracts/ERC4626/properties/RedeemUsingApprovalProps.sol +++ b/contracts/ERC4626/properties/RedeemUsingApprovalProps.sol @@ -2,32 +2,49 @@ pragma solidity ^0.8.0; import {CryticERC4626PropertyBase} from "../util/ERC4626PropertyTestBase.sol"; import {CryticERC4626VaultProxy} from "./VaultProxy.sol"; -contract CryticERC4626RedeemUsingApproval is CryticERC4626PropertyBase, CryticERC4626VaultProxy { - +contract CryticERC4626RedeemUsingApproval is + CryticERC4626PropertyBase, + CryticERC4626VaultProxy +{ /// @notice verifies `redeem()` must allow proxies to redeem shares on behalf of the owner using share token approvals - /// verifies third party `redeem()` calls must update the msg.sender's allowance - function verify_redeemViaApprovalProxy(uint256 receiverId, uint256 shares) public returns (uint256 tokensWithdrawn){ + /// verifies third party `redeem()` calls must update the msg.sender's allowance + function verify_redeemViaApprovalProxy( + uint256 receiverId, + uint256 shares + ) public returns (uint256 tokensWithdrawn) { address owner = address(this); address receiver = restrictAddressToThirdParties(receiverId); - shares = requireValidRedeemAmount(owner, shares); + shares = requireValidRedeemAmount(owner, shares); vault.approve(address(redemptionProxy), shares); measureAddressHoldings(address(this), "vault", "before redeemption"); - try redemptionProxy.redeemOnBehalf(shares, receiver, owner) returns (uint256 _tokensWithdrawn){ + try redemptionProxy.redeemOnBehalf(shares, receiver, owner) returns ( + uint256 _tokensWithdrawn + ) { tokensWithdrawn = _tokensWithdrawn; } catch { - assertWithMsg(false, "vault.redeem() reverted during redeem via approval"); + assertWithMsg( + false, + "vault.redeem() reverted during redeem via approval" + ); } // verify allowance is updated - uint256 newAllowance = vault.allowance(owner,address(redemptionProxy)); - assertEq(newAllowance, 0, "The vault failed to update the redemption proxy's share allowance"); + uint256 newAllowance = vault.allowance(owner, address(redemptionProxy)); + assertEq( + newAllowance, + 0, + "The vault failed to update the redemption proxy's share allowance" + ); } /// @notice verifies `withdraw()` must allow proxies to withdraw shares on behalf of the owner using share token approvals - /// verifies third party `withdraw()` calls must update the msg.sender's allowance - function verify_withdrawViaApprovalProxy(uint256 receiverId, uint256 tokens) public returns (uint256 sharesBurned){ + /// verifies third party `withdraw()` calls must update the msg.sender's allowance + function verify_withdrawViaApprovalProxy( + uint256 receiverId, + uint256 tokens + ) public returns (uint256 sharesBurned) { address owner = address(this); address receiver = restrictAddressToThirdParties(receiverId); tokens = requireValidWithdrawAmount(owner, tokens); @@ -36,10 +53,15 @@ contract CryticERC4626RedeemUsingApproval is CryticERC4626PropertyBase, CryticER vault.approve(address(redemptionProxy), expectedSharesConsumed); measureAddressHoldings(address(this), "vault", "before withdraw"); - try redemptionProxy.withdrawOnBehalf(tokens, receiver, owner) returns (uint256 _sharesBurned) { + try redemptionProxy.withdrawOnBehalf(tokens, receiver, owner) returns ( + uint256 _sharesBurned + ) { sharesBurned = _sharesBurned; } catch { - assertWithMsg(false, "vault.withdraw() reverted during withdraw via approval"); + assertWithMsg( + false, + "vault.withdraw() reverted during withdraw via approval" + ); } emit LogUint256("withdraw consumed this many shares:", sharesBurned); @@ -48,39 +70,69 @@ contract CryticERC4626RedeemUsingApproval is CryticERC4626PropertyBase, CryticER uint256 newAllowance = vault.allowance(owner, address(redemptionProxy)); uint256 expectedAllowance = expectedSharesConsumed - sharesBurned; emit LogUint256("Expecting allowance to now be:", expectedAllowance); - assertEq(expectedAllowance, newAllowance, "The vault failed to update the redemption proxy's share allowance"); + assertEq( + expectedAllowance, + newAllowance, + "The vault failed to update the redemption proxy's share allowance" + ); } - /// @notice verifies third parties must not be able to `withdraw()` tokens on an owner's behalf without a token approval - function verify_withdrawRequiresTokenApproval(uint256 receiverId, uint256 tokens, uint256 sharesApproved) public { + /// @notice verifies third parties must not be able to `withdraw()` tokens on an owner's behalf without a token approval + function verify_withdrawRequiresTokenApproval( + uint256 receiverId, + uint256 tokens, + uint256 sharesApproved + ) public { address owner = address(this); address receiver = restrictAddressToThirdParties(receiverId); tokens = requireValidWithdrawAmount(owner, tokens); uint256 expectedSharesConsumed = vault.previewWithdraw(tokens); - emit LogUint256("Will attempt to proxy withdraw this many shares:", expectedSharesConsumed); + emit LogUint256( + "Will attempt to proxy withdraw this many shares:", + expectedSharesConsumed + ); require(sharesApproved < expectedSharesConsumed); emit LogUint256("Approving spend of this many shares:", sharesApproved); vault.approve(address(redemptionProxy), sharesApproved); - try redemptionProxy.withdrawOnBehalf(tokens, receiver, owner) returns (uint256 _sharesBurned) { - assertLte(_sharesBurned, sharesApproved, "Redemption proxy must not be able to withdraw more shares than it was approved"); - } catch { } + try redemptionProxy.withdrawOnBehalf(tokens, receiver, owner) returns ( + uint256 _sharesBurned + ) { + assertLte( + _sharesBurned, + sharesApproved, + "Redemption proxy must not be able to withdraw more shares than it was approved" + ); + } catch {} } - - /// @notice verifies third parties must not be able to `redeem()` shares on an owner's behalf without a token approval - function verify_redeemRequiresTokenApproval(uint256 receiverId, uint256 shares, uint256 sharesApproved) public { + + /// @notice verifies third parties must not be able to `redeem()` shares on an owner's behalf without a token approval + function verify_redeemRequiresTokenApproval( + uint256 receiverId, + uint256 shares, + uint256 sharesApproved + ) public { address owner = address(this); address receiver = restrictAddressToThirdParties(receiverId); shares = requireValidRedeemAmount(owner, shares); - emit LogUint256("Will attempt to proxy redeem this many shares:", shares); + emit LogUint256( + "Will attempt to proxy redeem this many shares:", + shares + ); require(sharesApproved < shares); emit LogUint256("Approving spend of this many shares:", sharesApproved); vault.approve(address(redemptionProxy), sharesApproved); - try redemptionProxy.redeemOnBehalf(shares, receiver, owner) returns (uint256 _sharesBurned) { - assertLte(_sharesBurned, sharesApproved, "Redemption proxy must not be able to redeem more shares than it was approved"); - } catch { } + try redemptionProxy.redeemOnBehalf(shares, receiver, owner) returns ( + uint256 _sharesBurned + ) { + assertLte( + _sharesBurned, + sharesApproved, + "Redemption proxy must not be able to redeem more shares than it was approved" + ); + } catch {} } } diff --git a/contracts/ERC4626/properties/RoundingProps.sol b/contracts/ERC4626/properties/RoundingProps.sol index 8f79710..058eda5 100644 --- a/contracts/ERC4626/properties/RoundingProps.sol +++ b/contracts/ERC4626/properties/RoundingProps.sol @@ -2,13 +2,19 @@ pragma solidity ^0.8.0; import {CryticERC4626PropertyBase} from "../util/ERC4626PropertyTestBase.sol"; import {CryticERC4626VaultProxy} from "./VaultProxy.sol"; -contract CryticERC4626Rounding is CryticERC4626PropertyBase, CryticERC4626VaultProxy { - +contract CryticERC4626Rounding is + CryticERC4626PropertyBase, + CryticERC4626VaultProxy +{ /// @notice verifies shares may never be minted for free using previewDeposit() function verify_previewDepositRoundingDirection() public { require(supportsInternalTestingIface); uint256 sharesMinted = vault.previewDeposit(0); - assertEq(sharesMinted, 0, "previewDeposit() must not mint shares at no cost"); + assertEq( + sharesMinted, + 0, + "previewDeposit() must not mint shares at no cost" + ); } /// @notice verifies shares may never be minted for free using previewMint() @@ -16,7 +22,11 @@ contract CryticERC4626Rounding is CryticERC4626PropertyBase, CryticERC4626VaultP require(supportsInternalTestingIface); require(shares > 0); uint256 tokensConsumed = vault.previewMint(shares); - assertGt(tokensConsumed, 0, "previewMint() must never mint shares at no cost"); + assertGt( + tokensConsumed, + 0, + "previewMint() must never mint shares at no cost" + ); } /// @notice verifies shares may never be minted for free using convertToShares() @@ -24,14 +34,22 @@ contract CryticERC4626Rounding is CryticERC4626PropertyBase, CryticERC4626VaultP require(supportsInternalTestingIface); // note: the correctness of this property can't be tested using solmate as a reference impl. 0/n=0. best case scenario, some other property gets set off. uint256 tokensWithdrawn = vault.convertToShares(0); - assertEq(tokensWithdrawn, 0, "convertToShares() must not allow shares to be minted at no cost"); + assertEq( + tokensWithdrawn, + 0, + "convertToShares() must not allow shares to be minted at no cost" + ); } /// @notice verifies tokens may never be withdrawn for free using previewRedeem() function verify_previewRedeemRoundingDirection() public { require(supportsInternalTestingIface); uint256 tokensWithdrawn = vault.previewRedeem(0); - assertEq(tokensWithdrawn, 0, "previewRedeem() must not allow assets to be withdrawn at no cost"); + assertEq( + tokensWithdrawn, + 0, + "previewRedeem() must not allow assets to be withdrawn at no cost" + ); } /// @notice verifies tokens may never be withdrawn for free using previewWithdraw() @@ -39,7 +57,11 @@ contract CryticERC4626Rounding is CryticERC4626PropertyBase, CryticERC4626VaultP require(supportsInternalTestingIface); require(tokens > 0); uint256 sharesRedeemed = vault.previewWithdraw(tokens); - assertGt(sharesRedeemed, 0, "previewWithdraw() must not allow assets to be withdrawn at no cost"); + assertGt( + sharesRedeemed, + 0, + "previewWithdraw() must not allow assets to be withdrawn at no cost" + ); } /// @notice verifies tokens may never be withdrawn for free using convertToAssets() @@ -47,7 +69,11 @@ contract CryticERC4626Rounding is CryticERC4626PropertyBase, CryticERC4626VaultP require(supportsInternalTestingIface); // note: the correctness of this property can't be tested using solmate as a reference impl. 0/n=0. best case scenario, some other property gets set off. uint256 tokensWithdrawn = vault.convertToAssets(0); - assertEq(tokensWithdrawn, 0, "convertToAssets() must not allow assets to be withdrawn at no cost"); + assertEq( + tokensWithdrawn, + 0, + "convertToAssets() must not allow assets to be withdrawn at no cost" + ); } /// @notice Indirectly verifies the rounding direction of convertToShares/convertToAssets is correct by attempting to @@ -56,7 +82,11 @@ contract CryticERC4626Rounding is CryticERC4626PropertyBase, CryticERC4626VaultP require(supportsInternalTestingIface); uint256 sharesMinted = vault.convertToShares(amount); uint256 tokensWithdrawn = vault.convertToAssets(sharesMinted); - assertGte(amount, tokensWithdrawn, "A profit was extractable from a convertTo round trip (deposit, then withdraw)"); + assertGte( + amount, + tokensWithdrawn, + "A profit was extractable from a convertTo round trip (deposit, then withdraw)" + ); } /// @notice Indirectly verifies the rounding direction of convertToShares/convertToAssets is correct by attempting to @@ -65,7 +95,11 @@ contract CryticERC4626Rounding is CryticERC4626PropertyBase, CryticERC4626VaultP require(supportsInternalTestingIface); uint256 tokensWithdrawn = vault.convertToAssets(amount); uint256 sharesMinted = vault.convertToShares(tokensWithdrawn); - assertGte(amount, sharesMinted, "A profit was extractable from a convertTo round trip (withdraw, then deposit)"); + assertGte( + amount, + sharesMinted, + "A profit was extractable from a convertTo round trip (withdraw, then deposit)" + ); } /// @notice verifies Shares may never be minted for free using deposit() @@ -88,7 +122,11 @@ contract CryticERC4626Rounding is CryticERC4626PropertyBase, CryticERC4626VaultP function verify_withdrawRoundingDirection(uint256 tokens) public { require(supportsInternalTestingIface); require(tokens > 0); - uint256 sharesRedeemed = vault.withdraw(tokens, address(this), address(this)); + uint256 sharesRedeemed = vault.withdraw( + tokens, + address(this), + address(this) + ); assertGt(sharesRedeemed, 0, "Token must not be withdrawn for free"); } diff --git a/contracts/ERC4626/properties/SecurityProps.sol b/contracts/ERC4626/properties/SecurityProps.sol index 0cd659c..1813cdb 100644 --- a/contracts/ERC4626/properties/SecurityProps.sol +++ b/contracts/ERC4626/properties/SecurityProps.sol @@ -2,13 +2,20 @@ pragma solidity ^0.8.0; import {CryticERC4626PropertyBase} from "../util/ERC4626PropertyTestBase.sol"; contract CryticERC4626SecurityProps is CryticERC4626PropertyBase { - /// @notice verify `decimals()` should be larger than or equal to `asset.decimals()` + /// @notice verify `decimals()` should be larger than or equal to `asset.decimals()` function verify_assetDecimalsLessThanVault() public { - assertGte(vault.decimals(), asset.decimals(), "The vault's share token should have greater than or equal to the number of decimals as the vault's asset token."); + assertGte( + vault.decimals(), + asset.decimals(), + "The vault's share token should have greater than or equal to the number of decimals as the vault's asset token." + ); } - /// @notice verify Accounting system must not be vulnerable to share price inflation attacks - function verify_sharePriceInflationAttack(uint256 inflateAmount, uint256 delta) public { + /// @notice verify Accounting system must not be vulnerable to share price inflation attacks + function verify_sharePriceInflationAttack( + uint256 inflateAmount, + uint256 delta + ) public { // this has to be changed if there's deposit/withdraw fees uint256 lossThreshold = 0.999 ether; // vault is fresh @@ -28,7 +35,7 @@ contract CryticERC4626SecurityProps is CryticERC4626PropertyBase { require(vault.totalAssets() == 1); // inflate pps - asset.transfer(address(vault), inflateAmount-1); + asset.transfer(address(vault), inflateAmount - 1); // fund victim alice.fund(victimDeposit); @@ -38,17 +45,23 @@ contract CryticERC4626SecurityProps is CryticERC4626PropertyBase { uint256 aliceShares = alice.depositFunds(victimDeposit); emit LogUint256("Alice Shares:", aliceShares); uint256 aliceWithdrawnFunds = alice.redeemShares(aliceShares); - emit LogUint256("Amount of tokens alice withdrew:", aliceWithdrawnFunds); + emit LogUint256( + "Amount of tokens alice withdrew:", + aliceWithdrawnFunds + ); uint256 victimLoss = victimDeposit - aliceWithdrawnFunds; emit LogUint256("Alice Loss:", victimLoss); - uint256 minRedeemedAmountNorm = (victimDeposit * lossThreshold) / 1 ether; + uint256 minRedeemedAmountNorm = (victimDeposit * lossThreshold) / + 1 ether; emit LogUint256("lossThreshold", lossThreshold); emit LogUint256("minRedeemedAmountNorm", minRedeemedAmountNorm); - assertGt(aliceWithdrawnFunds, minRedeemedAmountNorm, "Share inflation attack possible, victim lost an amount over lossThreshold%"); + assertGt( + aliceWithdrawnFunds, + minRedeemedAmountNorm, + "Share inflation attack possible, victim lost an amount over lossThreshold%" + ); } - - } diff --git a/contracts/ERC4626/properties/SenderIndependentProps.sol b/contracts/ERC4626/properties/SenderIndependentProps.sol index 6feb7d3..d65b031 100644 --- a/contracts/ERC4626/properties/SenderIndependentProps.sol +++ b/contracts/ERC4626/properties/SenderIndependentProps.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; import {CryticERC4626PropertyBase} from "../util/ERC4626PropertyTestBase.sol"; contract CryticERC4626SenderIndependent is CryticERC4626PropertyBase { - // todo: these properties may have issues in vaults that have super weird redemption curves. + // todo: these properties may have issues in vaults that have super weird redemption curves. // If that happens, use a proxy contract to compare results instead of msg.sender's state /// @notice verify `maxDeposit()` assumes the receiver/sender has infinite assets @@ -11,7 +11,11 @@ contract CryticERC4626SenderIndependent is CryticERC4626PropertyBase { uint256 maxDepositBefore = vault.maxDeposit(receiver); asset.mint(receiver, tokens); uint256 maxDepositAfter = vault.maxDeposit(receiver); - assertEq(maxDepositBefore, maxDepositAfter, "maxDeposit must assume the agent has infinite assets"); + assertEq( + maxDepositBefore, + maxDepositAfter, + "maxDeposit must assume the agent has infinite assets" + ); } /// @notice verify `maxMint()` assumes the receiver/sender has infinite assets @@ -20,17 +24,28 @@ contract CryticERC4626SenderIndependent is CryticERC4626PropertyBase { uint256 maxMintBefore = vault.maxMint(receiver); asset.mint(receiver, tokens); uint256 maxMintAfter = vault.maxMint(receiver); - assertEq(maxMintBefore, maxMintAfter, "maxMint must assume the agent has infinite assets"); + assertEq( + maxMintBefore, + maxMintAfter, + "maxMint must assume the agent has infinite assets" + ); } /// @notice verify `previewMint()` does not account for msg.sender asset balance - function verify_previewMintIgnoresSender(uint256 tokens, uint256 shares) public { + function verify_previewMintIgnoresSender( + uint256 tokens, + uint256 shares + ) public { address receiver = address(this); uint256 assetsExpectedBefore = vault.previewMint(shares); prepareAddressForDeposit(receiver, tokens); uint256 assetsExpectedAfter = vault.previewMint(shares); - assertEq(assetsExpectedBefore, assetsExpectedAfter, "previewMint must not be dependent on msg.sender"); + assertEq( + assetsExpectedBefore, + assetsExpectedAfter, + "previewMint must not be dependent on msg.sender" + ); } /// @notice verify `previewDeposit()` does not account for msg.sender asset balance @@ -40,7 +55,11 @@ contract CryticERC4626SenderIndependent is CryticERC4626PropertyBase { prepareAddressForDeposit(receiver, tokens); uint256 sharesExpectedAfter = vault.previewDeposit(tokens); - assertEq(sharesExpectedBefore, sharesExpectedAfter, "previewDeposit must not be dependent on msg.sender"); + assertEq( + sharesExpectedBefore, + sharesExpectedAfter, + "previewDeposit must not be dependent on msg.sender" + ); } /// @notice verify `previewWithdraw()` does not account for msg.sender asset balance @@ -52,12 +71,16 @@ contract CryticERC4626SenderIndependent is CryticERC4626PropertyBase { vault.deposit(tokens, receiver); uint256 sharesExpectedAfter = vault.previewWithdraw(tokens); - assertEq(sharesExpectedBefore, sharesExpectedAfter, "previewWithdraw must not be dependent on msg.sender"); + assertEq( + sharesExpectedBefore, + sharesExpectedAfter, + "previewWithdraw must not be dependent on msg.sender" + ); // keep this property relatively stateless vault.redeem(vault.balanceOf(receiver), receiver, receiver); } - + /// @notice verify `previewRedeem()` does not account for msg.sender asset balance function verify_previewRedeemIgnoresSender(uint256 shares) public { address receiver = address(this); @@ -65,11 +88,15 @@ contract CryticERC4626SenderIndependent is CryticERC4626PropertyBase { uint256 assetsToDeposit = vault.previewMint(shares); prepareAddressForDeposit(receiver, assetsToDeposit); - + vault.deposit(assetsToDeposit, receiver); uint256 tokensExpectedAfter = vault.previewRedeem(shares); - assertEq(tokensExpectedBefore, tokensExpectedAfter, "previewRedeem must not be dependent on msg.sender"); + assertEq( + tokensExpectedBefore, + tokensExpectedAfter, + "previewRedeem must not be dependent on msg.sender" + ); // keep this property relatively stateless vault.redeem(vault.balanceOf(receiver), receiver, receiver); diff --git a/contracts/ERC4626/properties/VaultProxy.sol b/contracts/ERC4626/properties/VaultProxy.sol index c8c7984..afca0d3 100644 --- a/contracts/ERC4626/properties/VaultProxy.sol +++ b/contracts/ERC4626/properties/VaultProxy.sol @@ -33,7 +33,11 @@ contract CryticERC4626VaultProxy is CryticERC4626PropertyBase { vault.deposit(assets, receiver); } - function withdraw(uint256 assets, uint256 ownerId, uint256 receiverId) public { + function withdraw( + uint256 assets, + uint256 ownerId, + uint256 receiverId + ) public { address receiver = restrictAddressToThirdParties(receiverId); address owner = restrictAddressToThirdParties(ownerId); vault.withdraw(assets, receiver, owner); @@ -44,7 +48,11 @@ contract CryticERC4626VaultProxy is CryticERC4626PropertyBase { vault.mint(shares, receiver); } - function redeem(uint256 shares, uint256 ownerId, uint256 receiverId) public { + function redeem( + uint256 shares, + uint256 ownerId, + uint256 receiverId + ) public { address receiver = restrictAddressToThirdParties(receiverId); address owner = restrictAddressToThirdParties(ownerId); vault.redeem(shares, receiver, owner); diff --git a/contracts/ERC4626/test/Solmate4626.sol b/contracts/ERC4626/test/Solmate4626.sol index ffb94e6..c70d1c6 100644 --- a/contracts/ERC4626/test/Solmate4626.sol +++ b/contracts/ERC4626/test/Solmate4626.sol @@ -8,25 +8,25 @@ import {CryticERC4626SecurityProps} from "../properties/SecurityProps.sol"; /// @notice Basic solmate 4626 impl for property validation/testing. contract Solmate4626 is ERC4626 { - uint256 private _totalAssets; + uint256 private _totalAssets; - constructor(ERC20 _asset) ERC4626(_asset, "Test Vault",_asset.symbol()) {} + constructor(ERC20 _asset) ERC4626(_asset, "Test Vault", _asset.symbol()) {} - function totalAssets() public view virtual override returns (uint256) { - return _totalAssets; - } + function totalAssets() public view virtual override returns (uint256) { + return _totalAssets; + } - function beforeWithdraw(uint256 assets, uint256) internal override { - _totalAssets = _totalAssets - assets; - } + function beforeWithdraw(uint256 assets, uint256) internal override { + _totalAssets = _totalAssets - assets; + } - function afterDeposit(uint256 assets, uint256) internal override { - _totalAssets = _totalAssets + assets; - } + function afterDeposit(uint256 assets, uint256) internal override { + _totalAssets = _totalAssets + assets; + } } -contract TestHarness is CryticERC4626PropertyTests{ - constructor () { +contract TestHarness is CryticERC4626PropertyTests { + constructor() { TestERC20Token _asset = new TestERC20Token("Test Token", "TT", 18); ERC4626 _vault = new Solmate4626(ERC20(address(_asset))); initialize(address(_vault), address(_asset)); diff --git a/contracts/ERC4626/test/echidna.config.yaml b/contracts/ERC4626/test/echidna.config.yaml index 5984852..f2b3a66 100644 --- a/contracts/ERC4626/test/echidna.config.yaml +++ b/contracts/ERC4626/test/echidna.config.yaml @@ -1,7 +1,7 @@ -coverage: true +coverage: true corpusDir: "corpus" testMode: assertion testLimit: 100000 cryticArgs: - - --solc-remaps - - ds-test/=lib/forge-std/lib/ds-test/src/ forge-std/=lib/forge-std/src/ solmate/=lib/solmate/ \ No newline at end of file + - --solc-remaps + - ds-test/=lib/forge-std/lib/ds-test/src/ forge-std/=lib/forge-std/src/ solmate/=lib/solmate/ diff --git a/contracts/ERC4626/test/rounding/BadConvertToAssetsRounding.sol b/contracts/ERC4626/test/rounding/BadConvertToAssetsRounding.sol index 6020a1e..7dd3e8e 100644 --- a/contracts/ERC4626/test/rounding/BadConvertToAssetsRounding.sol +++ b/contracts/ERC4626/test/rounding/BadConvertToAssetsRounding.sol @@ -14,44 +14,44 @@ import {CryticERC4626Rounding} from "../../properties/RoundingProps.sol"; import {CryticERC4626VaultProxy} from "../../properties/VaultProxy.sol"; contract BadConvertToAssetsRounding is ERC4626 { - using FixedPointMathLib for uint256; + using FixedPointMathLib for uint256; - uint256 private _totalAssets; - - constructor(ERC20 _asset) ERC4626(_asset,"Test Vault", _asset.symbol()) { - } + uint256 private _totalAssets; - function totalAssets() public view virtual override returns (uint256) { - return _totalAssets; - } + constructor(ERC20 _asset) ERC4626(_asset, "Test Vault", _asset.symbol()) {} - function beforeWithdraw(uint256 assets, uint256) internal override { - _totalAssets = _totalAssets - assets; - } + function totalAssets() public view virtual override returns (uint256) { + return _totalAssets; + } - function afterDeposit(uint256 assets, uint256) internal override { - _totalAssets = _totalAssets + assets; - } + function beforeWithdraw(uint256 assets, uint256) internal override { + _totalAssets = _totalAssets - assets; + } - function convertToAssets(uint256 shares) public view override returns (uint256) { - uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. - return supply == 0 ? shares : shares.mulDivUp(totalAssets(), supply); - } + function afterDeposit(uint256 assets, uint256) internal override { + _totalAssets = _totalAssets + assets; + } - function recognizeProfit(uint256 profit) public { - TestERC20Token(address(asset)).mint(address(this), profit); - _totalAssets += profit; - } + function convertToAssets( + uint256 shares + ) public view override returns (uint256) { + uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. + return supply == 0 ? shares : shares.mulDivUp(totalAssets(), supply); + } - function recognizeLoss(uint256 loss) public { - TestERC20Token(address(asset)).burn(address(this), loss); - _totalAssets -= loss; - } + function recognizeProfit(uint256 profit) public { + TestERC20Token(address(asset)).mint(address(this), profit); + _totalAssets += profit; + } + function recognizeLoss(uint256 loss) public { + TestERC20Token(address(asset)).burn(address(this), loss); + _totalAssets -= loss; + } } -contract TestHarness is CryticERC4626Rounding{ - constructor () { +contract TestHarness is CryticERC4626Rounding { + constructor() { TestERC20Token _asset = new TestERC20Token("Test Token", "TT", 18); ERC4626 _vault = new BadConvertToAssetsRounding(ERC20(address(_asset))); initialize(address(_vault), address(_asset), true); diff --git a/contracts/ERC4626/test/rounding/BadConvertToSharesRounding.sol b/contracts/ERC4626/test/rounding/BadConvertToSharesRounding.sol index 3e21439..126d053 100644 --- a/contracts/ERC4626/test/rounding/BadConvertToSharesRounding.sol +++ b/contracts/ERC4626/test/rounding/BadConvertToSharesRounding.sol @@ -9,48 +9,49 @@ import {CryticERC4626PropertyTests} from "../../ERC4626PropertyTests.sol"; import {CryticERC4626Rounding} from "../../properties/RoundingProps.sol"; contract BadConvertToSharesRounding is ERC4626 { - using FixedPointMathLib for uint256; - - uint256 private _totalAssets; - - constructor(ERC20 _asset) ERC4626(_asset, "Test Vault", _asset.symbol()) { - } - - function totalAssets() public view virtual override returns (uint256) { - return _totalAssets; - } - - function beforeWithdraw(uint256 assets, uint256) internal override { - _totalAssets = _totalAssets - assets; - } - - function afterDeposit(uint256 assets, uint256) internal override { - _totalAssets = _totalAssets + assets; - } - - function convertToShares(uint256 assets) public view override returns (uint256) { - uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. - - if(supply == 0){ - return assets; - } else { - return assets.mulDivUp(supply, totalAssets()); - } - } - - function recognizeProfit(uint256 profit) public { - TestERC20Token(address(asset)).mint(address(this), profit); - _totalAssets += profit; - } - - function recognizeLoss(uint256 loss) public { - TestERC20Token(address(asset)).burn(address(this), loss); - _totalAssets -= loss; - } + using FixedPointMathLib for uint256; + + uint256 private _totalAssets; + + constructor(ERC20 _asset) ERC4626(_asset, "Test Vault", _asset.symbol()) {} + + function totalAssets() public view virtual override returns (uint256) { + return _totalAssets; + } + + function beforeWithdraw(uint256 assets, uint256) internal override { + _totalAssets = _totalAssets - assets; + } + + function afterDeposit(uint256 assets, uint256) internal override { + _totalAssets = _totalAssets + assets; + } + + function convertToShares( + uint256 assets + ) public view override returns (uint256) { + uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. + + if (supply == 0) { + return assets; + } else { + return assets.mulDivUp(supply, totalAssets()); + } + } + + function recognizeProfit(uint256 profit) public { + TestERC20Token(address(asset)).mint(address(this), profit); + _totalAssets += profit; + } + + function recognizeLoss(uint256 loss) public { + TestERC20Token(address(asset)).burn(address(this), loss); + _totalAssets -= loss; + } } -contract TestHarness is CryticERC4626Rounding{ - constructor () { +contract TestHarness is CryticERC4626Rounding { + constructor() { TestERC20Token _asset = new TestERC20Token("Test Token", "TT", 18); ERC4626 _vault = new BadConvertToSharesRounding(ERC20(address(_asset))); initialize(address(_vault), address(_asset), true); diff --git a/contracts/ERC4626/test/rounding/BadPreviewMintRounding.sol b/contracts/ERC4626/test/rounding/BadPreviewMintRounding.sol index c93a221..545eedb 100644 --- a/contracts/ERC4626/test/rounding/BadPreviewMintRounding.sol +++ b/contracts/ERC4626/test/rounding/BadPreviewMintRounding.sol @@ -9,48 +9,49 @@ import {TestERC20Token} from "../../util/TestERC20Token.sol"; import {CryticERC4626Rounding} from "../../properties/RoundingProps.sol"; contract BadPreviewMintRounding is ERC4626 { - using FixedPointMathLib for uint256; + using FixedPointMathLib for uint256; - uint256 private _totalAssets; - - constructor(ERC20 _asset) ERC4626(_asset,"Test Vault", _asset.symbol()) { - // initialize the vault such that price per share != 1:1 - _totalAssets = 0.001 ether; - totalSupply = 1 ether; - } + uint256 private _totalAssets; - function totalAssets() public view virtual override returns (uint256) { - return _totalAssets; - } + constructor(ERC20 _asset) ERC4626(_asset, "Test Vault", _asset.symbol()) { + // initialize the vault such that price per share != 1:1 + _totalAssets = 0.001 ether; + totalSupply = 1 ether; + } - function beforeWithdraw(uint256 assets, uint256) internal override { - _totalAssets = _totalAssets - assets; - } + function totalAssets() public view virtual override returns (uint256) { + return _totalAssets; + } - function afterDeposit(uint256 assets, uint256) internal override { - _totalAssets = _totalAssets + assets; - } + function beforeWithdraw(uint256 assets, uint256) internal override { + _totalAssets = _totalAssets - assets; + } - function previewMint(uint256 shares) public view override returns (uint256) { - uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. + function afterDeposit(uint256 assets, uint256) internal override { + _totalAssets = _totalAssets + assets; + } - return supply == 0 ? shares : shares.mulDivDown(totalAssets(), supply); - } + function previewMint( + uint256 shares + ) public view override returns (uint256) { + uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. - function recognizeProfit(uint256 profit) public { - TestERC20Token(address(asset)).mint(address(this), profit); - _totalAssets += profit; - } + return supply == 0 ? shares : shares.mulDivDown(totalAssets(), supply); + } - function recognizeLoss(uint256 loss) public { - TestERC20Token(address(asset)).burn(address(this), loss); - _totalAssets -= loss; - } + function recognizeProfit(uint256 profit) public { + TestERC20Token(address(asset)).mint(address(this), profit); + _totalAssets += profit; + } + function recognizeLoss(uint256 loss) public { + TestERC20Token(address(asset)).burn(address(this), loss); + _totalAssets -= loss; + } } -contract TestHarness is CryticERC4626Rounding{ - constructor () { +contract TestHarness is CryticERC4626Rounding { + constructor() { TestERC20Token _asset = new TestERC20Token("Test Token", "TT", 18); ERC4626 _vault = new BadPreviewMintRounding(ERC20(address(_asset))); initialize(address(_vault), address(_asset), true); diff --git a/contracts/ERC4626/test/rounding/BadPreviewWithdrawRounding.sol b/contracts/ERC4626/test/rounding/BadPreviewWithdrawRounding.sol index d3ac907..9938a8e 100644 --- a/contracts/ERC4626/test/rounding/BadPreviewWithdrawRounding.sol +++ b/contracts/ERC4626/test/rounding/BadPreviewWithdrawRounding.sol @@ -9,49 +9,51 @@ import {TestERC20Token} from "../../util/TestERC20Token.sol"; import {CryticERC4626Rounding} from "../../properties/RoundingProps.sol"; contract BadPreviewWithdrawRounding is ERC4626 { - using FixedPointMathLib for uint256; - - uint256 private _totalAssets; - - constructor(ERC20 _asset) ERC4626(_asset, "Test Vault", _asset.symbol()) { - // initialize the vault such that price per share != 1:1 - _totalAssets = 1 ether; - totalSupply = 0.001 ether; - } - - function totalAssets() public view virtual override returns (uint256) { - return _totalAssets; - } - - function beforeWithdraw(uint256 assets, uint256) internal override { - _totalAssets = _totalAssets - assets; - } - - function afterDeposit(uint256 assets, uint256) internal override { - _totalAssets = _totalAssets + assets; - } - - function previewWithdraw(uint256 assets) public view override returns (uint256) { - uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. - - return supply == 0 ? assets : assets.mulDivDown(supply, totalAssets()); - } - - function recognizeProfit(uint256 profit) public { - TestERC20Token(address(asset)).mint(address(this), profit); - _totalAssets += profit; - } - - function recognizeLoss(uint256 loss) public { - TestERC20Token(address(asset)).burn(address(this), loss); - _totalAssets -= loss; - } + using FixedPointMathLib for uint256; + + uint256 private _totalAssets; + + constructor(ERC20 _asset) ERC4626(_asset, "Test Vault", _asset.symbol()) { + // initialize the vault such that price per share != 1:1 + _totalAssets = 1 ether; + totalSupply = 0.001 ether; + } + + function totalAssets() public view virtual override returns (uint256) { + return _totalAssets; + } + + function beforeWithdraw(uint256 assets, uint256) internal override { + _totalAssets = _totalAssets - assets; + } + + function afterDeposit(uint256 assets, uint256) internal override { + _totalAssets = _totalAssets + assets; + } + + function previewWithdraw( + uint256 assets + ) public view override returns (uint256) { + uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. + + return supply == 0 ? assets : assets.mulDivDown(supply, totalAssets()); + } + + function recognizeProfit(uint256 profit) public { + TestERC20Token(address(asset)).mint(address(this), profit); + _totalAssets += profit; + } + + function recognizeLoss(uint256 loss) public { + TestERC20Token(address(asset)).burn(address(this), loss); + _totalAssets -= loss; + } } -contract TestHarness is CryticERC4626Rounding{ - constructor () { +contract TestHarness is CryticERC4626Rounding { + constructor() { TestERC20Token _asset = new TestERC20Token("Test Token", "TT", 18); ERC4626 _vault = new BadPreviewWithdrawRounding(ERC20(address(_asset))); - initialize(address(_vault),address(_asset), true); + initialize(address(_vault), address(_asset), true); } } diff --git a/contracts/ERC4626/test/security/BadShareInflation.sol b/contracts/ERC4626/test/security/BadShareInflation.sol index a34294f..11556c6 100644 --- a/contracts/ERC4626/test/security/BadShareInflation.sol +++ b/contracts/ERC4626/test/security/BadShareInflation.sol @@ -8,23 +8,21 @@ import {CryticERC4626SecurityProps} from "../../properties/SecurityProps.sol"; /// @notice Basic solmate 4626 impl for property validation/testing. contract BadShareInflation is ERC4626 { - uint256 private _totalAssets; + uint256 private _totalAssets; - constructor(ERC20 _asset) ERC4626(_asset, "Test Vault", _asset.symbol()) {} + constructor(ERC20 _asset) ERC4626(_asset, "Test Vault", _asset.symbol()) {} - function totalAssets() public view virtual override returns (uint256) { - return asset.balanceOf(address(this)); - } + function totalAssets() public view virtual override returns (uint256) { + return asset.balanceOf(address(this)); + } - function beforeWithdraw(uint256 assets, uint256) internal override { - } + function beforeWithdraw(uint256 assets, uint256) internal override {} - function afterDeposit(uint256 assets, uint256) internal override { - } + function afterDeposit(uint256 assets, uint256) internal override {} } contract TestHarness is CryticERC4626SecurityProps { - constructor () { + constructor() { TestERC20Token _asset = new TestERC20Token("Test Token", "TT", 18); ERC4626 _vault = new BadShareInflation(ERC20(address(_asset))); initialize(address(_vault), address(_asset), false); diff --git a/contracts/ERC4626/test/usingApproval/BadAllowanceUpdate.sol b/contracts/ERC4626/test/usingApproval/BadAllowanceUpdate.sol index 5884c7b..3810adf 100644 --- a/contracts/ERC4626/test/usingApproval/BadAllowanceUpdate.sol +++ b/contracts/ERC4626/test/usingApproval/BadAllowanceUpdate.sol @@ -14,12 +14,11 @@ contract BadAllowanceUpdate is ERC4626 { using SafeTransferLib for ERC20; uint256 private _totalAssets; - - constructor(ERC20 _asset) ERC4626(_asset, "Test Vault", _asset.symbol()) { - } + + constructor(ERC20 _asset) ERC4626(_asset, "Test Vault", _asset.symbol()) {} function totalAssets() public view virtual override returns (uint256) { - return _totalAssets; + return _totalAssets; } function beforeWithdraw(uint256 assets, uint256) internal override { @@ -35,13 +34,13 @@ contract BadAllowanceUpdate is ERC4626 { address receiver, address owner ) public override returns (uint256 shares) { - shares = previewWithdraw(assets); // No need to check for rounding error, previewWithdraw rounds up. + shares = previewWithdraw(assets); // No need to check for rounding error, previewWithdraw rounds up. - if (msg.sender != owner) { - uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals. + if (msg.sender != owner) { + uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals. - // if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares; - } + // if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares; + } beforeWithdraw(assets, shares); @@ -76,8 +75,8 @@ contract BadAllowanceUpdate is ERC4626 { } } -contract TestHarness is CryticERC4626RedeemUsingApproval{ - constructor () { +contract TestHarness is CryticERC4626RedeemUsingApproval { + constructor() { TestERC20Token _asset = new TestERC20Token("Test Token", "TT", 18); ERC4626 _vault = new BadAllowanceUpdate(ERC20(address(_asset))); initialize(address(_vault), address(_asset), false); diff --git a/contracts/ERC4626/util/Actor.sol b/contracts/ERC4626/util/Actor.sol index 9d61a15..d973dcd 100644 --- a/contracts/ERC4626/util/Actor.sol +++ b/contracts/ERC4626/util/Actor.sol @@ -21,14 +21,28 @@ contract Actor is PropertiesAsserts { token = TestERC20Token(address(_vault.asset())); } - function accountForOpenedPosition(uint256 _tokensDeposited, uint256 _sharesMinted) internal { + function accountForOpenedPosition( + uint256 _tokensDeposited, + uint256 _sharesMinted + ) internal { tokensDeposited += _tokensDeposited; sharesMinted += _sharesMinted; } - function accountForClosedPosition(uint256 _tokensReceived, uint256 _sharesBurned) internal { - assertLte(_sharesBurned, sharesMinted, "Actor has burned more shares than they ever minted. Implies a rounding or accounting error"); - assertLte(_tokensReceived, tokensDeposited, "Actor has withdrawn more tokens than they ever deposited. Implies a rounding or accounting error"); + function accountForClosedPosition( + uint256 _tokensReceived, + uint256 _sharesBurned + ) internal { + assertLte( + _sharesBurned, + sharesMinted, + "Actor has burned more shares than they ever minted. Implies a rounding or accounting error" + ); + assertLte( + _tokensReceived, + tokensDeposited, + "Actor has withdrawn more tokens than they ever deposited. Implies a rounding or accounting error" + ); tokensDeposited -= _tokensReceived; sharesMinted -= _sharesBurned; } @@ -41,40 +55,60 @@ contract Actor is PropertiesAsserts { token.approve(address(vault), type(uint256).max); } - function depositFunds(uint256 assets) public returns (uint256 _sharesMinted) { + function depositFunds( + uint256 assets + ) public returns (uint256 _sharesMinted) { _sharesMinted = vault.deposit(assets, address(this)); accountForOpenedPosition(assets, _sharesMinted); } - function mintShares(uint256 shares) public returns (uint256 _tokensDeposited) { + function mintShares( + uint256 shares + ) public returns (uint256 _tokensDeposited) { _tokensDeposited = vault.mint(shares, address(this)); accountForOpenedPosition(_tokensDeposited, shares); } - function withdrawTokens(uint256 assets) public returns (uint256 _sharesBurned) { + function withdrawTokens( + uint256 assets + ) public returns (uint256 _sharesBurned) { _sharesBurned = vault.withdraw(assets, address(this), address(this)); accountForClosedPosition(assets, _sharesBurned); } - function redeemShares(uint256 shares) public returns (uint256 _tokensWithdrawn) { + function redeemShares( + uint256 shares + ) public returns (uint256 _tokensWithdrawn) { _tokensWithdrawn = vault.redeem(shares, address(this), address(this)); accountForClosedPosition(_tokensWithdrawn, shares); } - function depositFundsOnBehalf(uint256 assets, address receiver) public returns (uint256 _sharesMinted) { + function depositFundsOnBehalf( + uint256 assets, + address receiver + ) public returns (uint256 _sharesMinted) { _sharesMinted = vault.deposit(assets, receiver); } - function mintSharesOnBehalf(uint256 shares, address receiver) public returns (uint256 _tokensDeposited) { + function mintSharesOnBehalf( + uint256 shares, + address receiver + ) public returns (uint256 _tokensDeposited) { _tokensDeposited = vault.mint(shares, receiver); } - function withdrawTokensOnBehalf(uint256 assets, address receiver) public returns (uint256 _sharesBurned) { + function withdrawTokensOnBehalf( + uint256 assets, + address receiver + ) public returns (uint256 _sharesBurned) { _sharesBurned = vault.withdraw(assets, receiver, address(this)); accountForClosedPosition(assets, _sharesBurned); } - function redeemSharesOnBehalf(uint256 shares, address receiver) public returns (uint256 _tokensWithdrawn) { + function redeemSharesOnBehalf( + uint256 shares, + address receiver + ) public returns (uint256 _tokensWithdrawn) { _tokensWithdrawn = vault.redeem(shares, receiver, address(this)); accountForClosedPosition(_tokensWithdrawn, shares); } diff --git a/contracts/ERC4626/util/ERC4626PropertyTestBase.sol b/contracts/ERC4626/util/ERC4626PropertyTestBase.sol index 3253913..c85ef02 100644 --- a/contracts/ERC4626/util/ERC4626PropertyTestBase.sol +++ b/contracts/ERC4626/util/ERC4626PropertyTestBase.sol @@ -20,7 +20,11 @@ contract CryticERC4626PropertyBase is PropertiesAsserts { // feature flags bool supportsInternalTestingIface; - function initialize(address _vault, address _asset, bool _supportsInternalTestingIface) internal { + function initialize( + address _vault, + address _asset, + bool _supportsInternalTestingIface + ) internal { vault = IERC4626(_vault); asset = TestERC20Token(_asset); alice = new Actor(vault); @@ -39,41 +43,51 @@ contract CryticERC4626PropertyBase is PropertiesAsserts { /// @param target A address to target /// @param name A name for the target address (alice, bob, vault, etc.) /// @param annotation An additional piece of metadata for debugging ie: "before deposit", "after mint", etc. - function measureAddressHoldings(address target, string memory name, string memory annotation) internal - returns (uint256 assetBalance, uint256 shareBalance) { - + function measureAddressHoldings( + address target, + string memory name, + string memory annotation + ) internal returns (uint256 assetBalance, uint256 shareBalance) { assetBalance = asset.balanceOf(target); shareBalance = vault.balanceOf(target); - - string memory assetMsg = string(abi.encodePacked("asset.balanceOf(", name, ") (", annotation, ")")); + + string memory assetMsg = string( + abi.encodePacked("asset.balanceOf(", name, ") (", annotation, ")") + ); emit LogUint256(assetMsg, assetBalance); - string memory shareMsg = string(abi.encodePacked("vault.balanceOf(", name, ") (", annotation, ")")); + string memory shareMsg = string( + abi.encodePacked("vault.balanceOf(", name, ") (", annotation, ")") + ); emit LogUint256(shareMsg, shareBalance); } /// @notice Prevents `party` from resolving to addresses which have special accounting rules. - function restrictAddressToThirdParties(uint256 partyIndex) internal view returns (address) { + function restrictAddressToThirdParties( + uint256 partyIndex + ) internal view returns (address) { // set up 3 static third parties partyIndex = partyIndex % 3; - if(partyIndex == 0){ + if (partyIndex == 0) { return address(this); } - if(partyIndex == 1){ + if (partyIndex == 1) { return 0xaAaAaAaaAaAaAaaAaAAAAAAAAaaaAaAaAaaAaaAa; } return 0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB; } - /// @notice Performs all the checks required to ensure a successful vault deposit. This includes funding the owner account and clamping token amounts as needed. /// It is assumed that successful calls to requireValidDepositAmount imply that vault.deposit() will not revert. This implied property might not hold for certain /// vault implementations, and should be modified if exceptions are discovered. - function requireValidDepositAmount(address owner, address receiver, uint256 tokens) internal returns (uint256) { + function requireValidDepositAmount( + address owner, + address receiver, + uint256 tokens + ) internal returns (uint256) { tokens = clampLte(tokens, vault.maxDeposit(receiver)); tokens = clampGt(tokens, 0); prepareAddressForDeposit(owner, tokens); - // The following logic is intended to revert when an unreasonably large deposit is being made. uint256 sharesMinted = vault.convertToShares(tokens); uint256 currentShares = vault.balanceOf(receiver); @@ -89,14 +103,18 @@ contract CryticERC4626PropertyBase is PropertiesAsserts { /// @notice Performs all the checks required to ensure a successful vault mint. This includes funding the owner account and clamping token amounts as needed. /// It is assumed that successful calls to requireValidDepositAmount imply that vault.mint() will not revert. This implied property might not hold for certain /// vault implementations, and should be modified if exceptions are discovered. - function requireValidMintAmount(address owner, address receiver, uint256 shares) internal returns (uint256) { + function requireValidMintAmount( + address owner, + address receiver, + uint256 shares + ) internal returns (uint256) { shares = clampLte(shares, vault.maxMint(receiver)); uint256 tokensDeposited = vault.previewMint(shares); prepareAddressForDeposit(owner, tokensDeposited); // The following logic is intended to revert when an unreasonably large mint is being made. uint256 currentShares = vault.balanceOf(receiver); - vault.previewRedeem(currentShares + shares ); + vault.previewRedeem(currentShares + shares); emit LogUint256("Shares to use in mint:", shares); // configure with setting? @@ -107,7 +125,10 @@ contract CryticERC4626PropertyBase is PropertiesAsserts { /// @notice Performs all the checks required to ensure a successful vault redeem. This includes funding the owner account and clamping token amounts as needed. /// It is assumed that successful calls to requireValidDepositAmount imply that vault.redeem() will not revert. This implied property might not hold for certain /// vault implementations, and should be modified if exceptions are discovered. - function requireValidRedeemAmount(address owner, uint256 shares) internal returns (uint256) { + function requireValidRedeemAmount( + address owner, + uint256 shares + ) internal returns (uint256) { // should this be a configured setting? require(shares > 0); @@ -131,10 +152,13 @@ contract CryticERC4626PropertyBase is PropertiesAsserts { /// @notice Performs all the checks required to ensure a successful vault withdraw. This includes funding the owner account and clamping token amounts as needed. /// It is assumed that successful calls to requireValidDepositAmount imply that vault.withdraw() will not revert. This implied property might not hold for certain /// vault implementations, and should be modified if exceptions are discovered. - function requireValidWithdrawAmount(address owner, uint256 tokens) internal returns (uint256) { + function requireValidWithdrawAmount( + address owner, + uint256 tokens + ) internal returns (uint256) { uint256 maxWithdraw = vault.maxWithdraw(owner); require(maxWithdraw > 0); - + uint256 ownerBalance = vault.balanceOf(owner); require(ownerBalance > 0); diff --git a/contracts/ERC4626/util/IERC4626Internal.sol b/contracts/ERC4626/util/IERC4626Internal.sol index 3e4a989..6aea402 100644 --- a/contracts/ERC4626/util/IERC4626Internal.sol +++ b/contracts/ERC4626/util/IERC4626Internal.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.0; interface CryticIERC4626Internal { /// @notice Called by the fuzzer. The vault implementation should use TestERC20Token.mint() to credit itself with the amount of profit. function recognizeProfit(uint256 profit) external; - + /// @notice Called by the fuzzer. The vault implementation should use TestERC20Token.burn()/.transfer() to account for the amount of loss. function recognizeLoss(uint256 loss) external; -} \ No newline at end of file +} diff --git a/contracts/ERC4626/util/RedemptionProxy.sol b/contracts/ERC4626/util/RedemptionProxy.sol index 007c28d..01d28be 100644 --- a/contracts/ERC4626/util/RedemptionProxy.sol +++ b/contracts/ERC4626/util/RedemptionProxy.sol @@ -9,11 +9,19 @@ contract RedemptionProxy { vault = _vault; } - function redeemOnBehalf(uint256 shares, address receiver, address owner) public returns (uint256 tokensWithdrawn) { - tokensWithdrawn = vault.redeem(shares, receiver, owner ); + function redeemOnBehalf( + uint256 shares, + address receiver, + address owner + ) public returns (uint256 tokensWithdrawn) { + tokensWithdrawn = vault.redeem(shares, receiver, owner); } - function withdrawOnBehalf(uint256 tokens, address receiver, address owner) public returns (uint256 sharesRedeemed) { - sharesRedeemed = vault.withdraw(tokens, receiver, owner ); + function withdrawOnBehalf( + uint256 tokens, + address receiver, + address owner + ) public returns (uint256 sharesRedeemed) { + sharesRedeemed = vault.withdraw(tokens, receiver, owner); } -} \ No newline at end of file +} diff --git a/contracts/ERC4626/util/TestERC20Token.sol b/contracts/ERC4626/util/TestERC20Token.sol index c410d9e..1072f22 100644 --- a/contracts/ERC4626/util/TestERC20Token.sol +++ b/contracts/ERC4626/util/TestERC20Token.sol @@ -1,76 +1,78 @@ pragma solidity ^0.8.0; contract TestERC20Token { + event Transfer(address indexed from, address indexed to, uint256 amount); + event Approval( + address indexed owner, + address indexed spender, + uint256 amount + ); + + string public name; + string public symbol; + uint256 public decimals; + uint256 public totalSupply; + mapping(address => uint256) public balanceOf; + mapping(address => mapping(address => uint256)) public allowance; + + constructor(string memory _name, string memory _symbol, uint256 _decimals) { + name = _name; + symbol = _symbol; + decimals = _decimals; + } - event Transfer(address indexed from, address indexed to, uint256 amount); - event Approval(address indexed owner, address indexed spender, uint256 amount); - - string public name; - string public symbol; - uint256 public decimals; - uint256 public totalSupply; - mapping(address => uint256) public balanceOf; - mapping(address => mapping(address => uint256)) public allowance; - - constructor( - string memory _name, - string memory _symbol, - uint256 _decimals - ) { - name = _name; - symbol = _symbol; - decimals = _decimals; - } - - function approve(address spender, uint256 amount) public returns (bool) { - allowance[msg.sender][spender] = amount; - - emit Approval(msg.sender, spender, amount); - return true; - } - - function transfer(address to, uint256 amount) public returns (bool) { - balanceOf[msg.sender] -= amount; - balanceOf[to] += amount; - - emit Transfer(msg.sender, to, amount); - return true; - } + function approve(address spender, uint256 amount) public returns (bool) { + allowance[msg.sender][spender] = amount; - function transferFrom( - address from, - address to, - uint256 amount) - public returns (bool) { + emit Approval(msg.sender, spender, amount); + return true; + } - uint256 spenderAllowance = allowance[from][msg.sender]; - if (spenderAllowance != type(uint256).max) { - allowance[from][msg.sender] = spenderAllowance - amount; - } + function transfer(address to, uint256 amount) public returns (bool) { + balanceOf[msg.sender] -= amount; + balanceOf[to] += amount; - balanceOf[from] -= amount; - balanceOf[to] += amount; + emit Transfer(msg.sender, to, amount); + return true; + } - emit Transfer(from, to, amount); - return true; + function transferFrom( + address from, + address to, + uint256 amount + ) public returns (bool) { + uint256 spenderAllowance = allowance[from][msg.sender]; + if (spenderAllowance != type(uint256).max) { + allowance[from][msg.sender] = spenderAllowance - amount; + } + + balanceOf[from] -= amount; + balanceOf[to] += amount; + + emit Transfer(from, to, amount); + return true; } - function mint(address to, uint256 amount) public { - totalSupply += amount; - balanceOf[to] += amount; + function mint(address to, uint256 amount) public { + totalSupply += amount; + balanceOf[to] += amount; - emit Transfer(address(0), to, amount); - } + emit Transfer(address(0), to, amount); + } - function burn(address from, uint256 amount) public { - totalSupply -= amount; - balanceOf[from] -= amount; + function burn(address from, uint256 amount) public { + totalSupply -= amount; + balanceOf[from] -= amount; - emit Transfer(from, address(0), amount); - } + emit Transfer(from, address(0), amount); + } - function forceApproval(address account, address spender, uint256 amount) public { - allowance[account][spender] = amount; - emit Approval(account, spender, amount); - } -} \ No newline at end of file + function forceApproval( + address account, + address spender, + uint256 amount + ) public { + allowance[account][spender] = amount; + emit Approval(account, spender, amount); + } +} diff --git a/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol b/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol index ffca89a..3246adc 100644 --- a/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol +++ b/contracts/Math/ABDKMath64x64/ABDKMath64x64PropertyTests.sol @@ -3,7 +3,6 @@ pragma solidity ^0.8.0; import "./abdk-libraries-solidity/ABDKMath64x64.sol"; contract CryticABDKMath64x64Properties { - /* ================================================================ 64x64 fixed-point constants used for testing specific values. This assumes that ABDK library's fromInt(x) works as expected. @@ -17,7 +16,8 @@ contract CryticABDKMath64x64Properties { int128 internal THOUSAND_FP = ABDKMath64x64.fromInt(1000); int128 internal MINUS_SIXTY_FOUR_FP = ABDKMath64x64.fromInt(-64); int128 internal EPSILON = 1; - int128 internal ONE_TENTH_FP = ABDKMath64x64.div(ABDKMath64x64.fromInt(1), ABDKMath64x64.fromInt(10)); + int128 internal ONE_TENTH_FP = + ABDKMath64x64.div(ABDKMath64x64.fromInt(1), ABDKMath64x64.fromInt(10)); /* ================================================================ Constants used for precision loss calculations @@ -50,28 +50,40 @@ contract CryticABDKMath64x64Properties { // These functions allows to compare a and b for equality, discarding // the last precision_bits bits. - // An absolute value function is implemented inline in order to not use + // An absolute value function is implemented inline in order to not use // the implementation from the library under test. - function equal_within_precision(int128 a, int128 b, uint256 precision_bits) public pure returns(bool) { + function equal_within_precision( + int128 a, + int128 b, + uint256 precision_bits + ) public pure returns (bool) { int128 max = (a > b) ? a : b; int128 min = (a > b) ? b : a; int128 r = (max - min) >> precision_bits; - + return (r == 0); } - function equal_within_precision_u(uint256 a, uint256 b, uint256 precision_bits) public pure returns(bool) { + function equal_within_precision_u( + uint256 a, + uint256 b, + uint256 precision_bits + ) public pure returns (bool) { uint256 max = (a > b) ? a : b; uint256 min = (a > b) ? b : a; uint256 r = (max - min) >> precision_bits; - + return (r == 0); } // This function determines if the relative error between a and b is less // than error_percent % (expressed as a 64x64 value) // Uses functions from the library under test! - function equal_within_tolerance(int128 a, int128 b, int128 error_percent) public pure returns(bool) { + function equal_within_tolerance( + int128 a, + int128 b, + int128 error_percent + ) public pure returns (bool) { int128 tol_value = abs(mul(a, div(error_percent, fromUInt(100)))); return (abs(sub(b, a)) <= tol_value); @@ -79,19 +91,25 @@ contract CryticABDKMath64x64Properties { // Check that there are remaining significant digits after a multiplication // Uses functions from the library under test! - function significant_digits_lost_in_mult(int128 a, int128 b) public pure returns (bool) { + function significant_digits_lost_in_mult( + int128 a, + int128 b + ) public pure returns (bool) { int128 x = a >= 0 ? a : -a; int128 y = b >= 0 ? b : -b; int128 lx = toInt(log_2(x)); int128 ly = toInt(log_2(y)); - return(lx + ly - 1 <= -64); + return (lx + ly - 1 <= -64); } // Return how many significant bits will remain after multiplying a and b // Uses functions from the library under test! - function significant_bits_after_mult(int128 a, int128 b) public pure returns (uint256) { + function significant_bits_after_mult( + int128 a, + int128 b + ) public pure returns (uint256) { int128 x = a >= 0 ? a : -a; int128 y = b >= 0 ? b : -b; @@ -100,37 +118,48 @@ contract CryticABDKMath64x64Properties { int256 prec = lx + ly - 1; if (prec < -64) return 0; - else return(64 + uint256(prec)); + else return (64 + uint256(prec)); } // Return the i most significant bits from |n|. If n has less than i significant bits, return |n| // Uses functions from the library under test! - function most_significant_bits(int128 n, uint256 i) public pure returns (uint256) { + function most_significant_bits( + int128 n, + uint256 i + ) public pure returns (uint256) { // Create a mask consisting of i bits set to 1 - uint256 mask = (2**i) - 1; + uint256 mask = (2 ** i) - 1; // Get the position of the MSB set to 1 of n uint256 pos = uint64(toInt(log_2(n)) + 64 + 1); // Get the positive value of n - uint256 value = (n>0) ? uint128(n) : uint128(-n); + uint256 value = (n > 0) ? uint128(n) : uint128(-n); // Shift the mask to match the rightmost 1-set bit - if(pos > i) { mask <<= (pos - i); } + if (pos > i) { + mask <<= (pos - i); + } return (value & mask); } - // Returns true if the n most significant bits of a and b are almost equal + // Returns true if the n most significant bits of a and b are almost equal // Uses functions from the library under test! - function equal_most_significant_bits_within_precision(int128 a, int128 b, uint256 bits) public pure returns (bool) { + function equal_most_significant_bits_within_precision( + int128 a, + int128 b, + uint256 bits + ) public pure returns (bool) { // Get the number of bits in a and b // Since log(x) returns in the interval [-64, 63), add 64 to be in the interval [0, 127) uint256 a_bits = uint256(int256(toInt(log_2(a)) + 64)); uint256 b_bits = uint256(int256(toInt(log_2(b)) + 64)); // a and b lengths may differ in 1 bit, so the shift should take into account the longest - uint256 shift_bits = (a_bits > b_bits) ? (a_bits - bits) : (b_bits - bits); + uint256 shift_bits = (a_bits > b_bits) + ? (a_bits - bits) + : (b_bits - bits); // Get the _bits_ most significant bits of a and b uint256 a_msb = most_significant_bits(a, bits) >> shift_bits; @@ -229,13 +258,10 @@ contract CryticABDKMath64x64Properties { return ABDKMath64x64.exp(x); } - /* ================================================================ Start of tests ================================================================ */ - - /* ================================================================ TESTS FOR FUNCTION add() @@ -289,7 +315,6 @@ contract CryticABDKMath64x64Properties { } } - /* ================================================================ Tests for overflow and edge cases. These should make sure that the function reverts on overflow and @@ -352,8 +377,6 @@ contract CryticABDKMath64x64Properties { } } - - /* ================================================================ TESTS FOR FUNCTION sub() @@ -381,7 +404,7 @@ contract CryticABDKMath64x64Properties { function sub_test_non_commutative(int128 x, int128 y) public pure { int128 x_y = sub(x, y); int128 y_x = sub(y, x); - + assert(x_y == neg(y_x)); } @@ -402,7 +425,7 @@ contract CryticABDKMath64x64Properties { int128 x_minus_y_plus_y = add(x_minus_y, y); int128 x_plus_y_minus_y = sub(x_plus_y, y); - + assert(x_minus_y_plus_y == x_plus_y_minus_y); assert(x_minus_y_plus_y == x); } @@ -450,7 +473,7 @@ contract CryticABDKMath64x64Properties { } } - // Subtracting minus one from the maximum value should revert, + // Subtracting minus one from the maximum value should revert, // as it is out of range function sub_test_maximum_value_minus_neg_one() public view { try this.sub(MAX_64x64, MINUS_ONE_FP) { @@ -482,8 +505,6 @@ contract CryticABDKMath64x64Properties { } } - - /* ================================================================ TESTS FOR FUNCTION mul() @@ -516,8 +537,12 @@ contract CryticABDKMath64x64Properties { // Failure if all significant digits are lost require(significant_bits_after_mult(x, y) > REQUIRED_SIGNIFICANT_BITS); require(significant_bits_after_mult(y, z) > REQUIRED_SIGNIFICANT_BITS); - require(significant_bits_after_mult(x_y, z) > REQUIRED_SIGNIFICANT_BITS); - require(significant_bits_after_mult(x, y_z) > REQUIRED_SIGNIFICANT_BITS); + require( + significant_bits_after_mult(x_y, z) > REQUIRED_SIGNIFICANT_BITS + ); + require( + significant_bits_after_mult(x, y_z) > REQUIRED_SIGNIFICANT_BITS + ); assert(equal_within_tolerance(xy_z, x_yz, ONE_TENTH_FP)); } @@ -534,9 +559,17 @@ contract CryticABDKMath64x64Properties { // Failure if all significant digits are lost require(significant_bits_after_mult(x, y) > REQUIRED_SIGNIFICANT_BITS); require(significant_bits_after_mult(x, z) > REQUIRED_SIGNIFICANT_BITS); - require(significant_bits_after_mult(x, y_plus_z) > REQUIRED_SIGNIFICANT_BITS); + require( + significant_bits_after_mult(x, y_plus_z) > REQUIRED_SIGNIFICANT_BITS + ); - assert(equal_within_tolerance(add(x_times_y, x_times_z), x_times_y_plus_z, ONE_TENTH_FP)); + assert( + equal_within_tolerance( + add(x_times_y, x_times_z), + x_times_y_plus_z, + ONE_TENTH_FP + ) + ); } // Test for identity operation @@ -572,7 +605,6 @@ contract CryticABDKMath64x64Properties { } } } - /* ================================================================ Tests for overflow and edge cases. @@ -618,8 +650,6 @@ contract CryticABDKMath64x64Properties { } } - - /* ================================================================ TESTS FOR FUNCTION div() @@ -649,7 +679,6 @@ contract CryticABDKMath64x64Properties { // The only allowed case to revert is if x == 0 assert(x == ZERO_FP); } - } // Test for negative divisor @@ -673,7 +702,7 @@ contract CryticABDKMath64x64Properties { assert(ZERO_FP == div_0); } - // Test that the absolute value of the result increases or + // Test that the absolute value of the result increases or // decreases depending on the denominator's absolute value function div_test_values(int128 x, int128 y) public view { require(y != ZERO_FP); @@ -738,8 +767,6 @@ contract CryticABDKMath64x64Properties { } } - - /* ================================================================ TESTS FOR FUNCTION neg() @@ -768,7 +795,6 @@ contract CryticABDKMath64x64Properties { assert(add(x, neg_x) == ZERO_FP); } - /* ================================================================ Tests for overflow and edge cases. These will make sure that the function reverts on overflow and @@ -803,8 +829,6 @@ contract CryticABDKMath64x64Properties { } } - - /* ================================================================ TESTS FOR FUNCTION abs() @@ -886,9 +910,7 @@ contract CryticABDKMath64x64Properties { // If it doesn't revert, the value must be MAX_64x64 abs_max = this.abs(MAX_64x64); assert(abs_max == MAX_64x64); - } catch { - - } + } catch {} } // Test the minimum value @@ -899,27 +921,22 @@ contract CryticABDKMath64x64Properties { // If it doesn't revert, the value must be the negative of MIN_64x64 abs_min = this.abs(MIN_64x64); assert(abs_min == neg(MIN_64x64)); - } catch { - - } + } catch {} } - - /* ================================================================ TESTS FOR FUNCTION inv() ================================================================ */ - /* ================================================================ Tests for mathematical properties. These should make sure that the implemented function complies with math rules and expected behaviour. ================================================================ */ - // Test that the inverse of the inverse is close enough to the + // Test that the inverse of the inverse is close enough to the // original number function inv_test_double_inverse(int128 x) public view { require(x != ZERO_FP); @@ -944,14 +961,21 @@ contract CryticABDKMath64x64Properties { // Test the anticommutativity of the division // x / y == 1 / (y / x) - function inv_test_division_noncommutativity(int128 x, int128 y) public view { + function inv_test_division_noncommutativity( + int128 x, + int128 y + ) public view { require(x != ZERO_FP && y != ZERO_FP); int128 x_y = div(x, y); int128 y_x = div(y, x); - require(significant_bits_after_mult(x, inv(y)) > REQUIRED_SIGNIFICANT_BITS); - require(significant_bits_after_mult(y, inv(x)) > REQUIRED_SIGNIFICANT_BITS); + require( + significant_bits_after_mult(x, inv(y)) > REQUIRED_SIGNIFICANT_BITS + ); + require( + significant_bits_after_mult(y, inv(x)) > REQUIRED_SIGNIFICANT_BITS + ); assert(equal_within_tolerance(x_y, inv(y_x), ONE_TENTH_FP)); } @@ -963,12 +987,15 @@ contract CryticABDKMath64x64Properties { int128 inv_x = inv(x); int128 inv_y = inv(y); int128 inv_x_times_inv_y = mul(inv_x, inv_y); - + int128 x_y = mul(x, y); int128 inv_x_y = inv(x_y); require(significant_bits_after_mult(x, y) > REQUIRED_SIGNIFICANT_BITS); - require(significant_bits_after_mult(inv_x, inv_y) > REQUIRED_SIGNIFICANT_BITS); + require( + significant_bits_after_mult(inv_x, inv_y) > + REQUIRED_SIGNIFICANT_BITS + ); // The maximum loss of precision is given by the formula: // 2 * | log_2(x) - log_2(y) | + 1 @@ -985,13 +1012,15 @@ contract CryticABDKMath64x64Properties { int128 inv_x = inv(x); int128 identity = mul(inv_x, x); - require(significant_bits_after_mult(x, inv_x) > REQUIRED_SIGNIFICANT_BITS); + require( + significant_bits_after_mult(x, inv_x) > REQUIRED_SIGNIFICANT_BITS + ); // They should agree with a tolerance of one tenth of a percent assert(equal_within_tolerance(identity, ONE_FP, ONE_TENTH_FP)); } - // Test that the absolute value of the result is in range zero-one + // Test that the absolute value of the result is in range zero-one // if x is greater than one, else, the absolute value of the result // must be greater than one function inv_test_values(int128 x) public view { @@ -999,7 +1028,7 @@ contract CryticABDKMath64x64Properties { int128 abs_inv_x = abs(inv(x)); - if(abs(x) >= ONE_FP) { + if (abs(x) >= ONE_FP) { assert(abs_inv_x <= ONE_FP); } else { assert(abs_inv_x > ONE_FP); @@ -1012,7 +1041,7 @@ contract CryticABDKMath64x64Properties { int128 inv_x = inv(x); - if(x > ZERO_FP) { + if (x > ZERO_FP) { assert(inv_x > ZERO_FP); } else { assert(inv_x < ZERO_FP); @@ -1030,9 +1059,7 @@ contract CryticABDKMath64x64Properties { try this.inv(ZERO_FP) { // Unexpected, the function must revert assert(false); - } catch { - - } + } catch {} } // Test the maximum value case, should not revert, and be close to zero @@ -1061,15 +1088,12 @@ contract CryticABDKMath64x64Properties { } } - - /* ================================================================ TESTS FOR FUNCTION avg() ================================================================ */ - /* ================================================================ Tests for arithmetic properties. These should make sure that the implemented function complies @@ -1081,7 +1105,7 @@ contract CryticABDKMath64x64Properties { function avg_test_values_in_range(int128 x, int128 y) public pure { int128 avg_xy = avg(x, y); - if(x >= y) { + if (x >= y) { assert(avg_xy >= y && avg_xy <= x); } else { assert(avg_xy >= x && avg_xy <= y); @@ -1101,11 +1125,10 @@ contract CryticABDKMath64x64Properties { function avg_test_operand_order(int128 x, int128 y) public pure { int128 avg_xy = avg(x, y); int128 avg_yx = avg(y, x); - + assert(avg_xy == avg_yx); } - /* ================================================================ Tests for overflow and edge cases. These will make sure that the function reverts on overflow and @@ -1114,41 +1137,34 @@ contract CryticABDKMath64x64Properties { // Test for the maximum value function avg_test_maximum() public view { - int128 result; + int128 result; // This may revert due to overflow depending on implementation // If it doesn't revert, the result must be MAX_64x64 try this.avg(MAX_64x64, MAX_64x64) { result = this.avg(MAX_64x64, MAX_64x64); assert(result == MAX_64x64); - } catch { - - } + } catch {} } // Test for the minimum value function avg_test_minimum() public view { - int128 result; + int128 result; // This may revert due to overflow depending on implementation // If it doesn't revert, the result must be MIN_64x64 try this.avg(MIN_64x64, MIN_64x64) { result = this.avg(MIN_64x64, MIN_64x64); assert(result == MIN_64x64); - } catch { - - } + } catch {} } - - /* ================================================================ TESTS FOR FUNCTION gavg() ================================================================ */ - /* ================================================================ Tests for arithmetic properties. These should make sure that the implemented function complies @@ -1160,10 +1176,10 @@ contract CryticABDKMath64x64Properties { function gavg_test_values_in_range(int128 x, int128 y) public view { int128 gavg_xy = gavg(x, y); - if(x == ZERO_FP || y == ZERO_FP) { + if (x == ZERO_FP || y == ZERO_FP) { assert(gavg_xy == ZERO_FP); } else { - if(abs(x) >= abs(y)) { + if (abs(x) >= abs(y)) { assert(gavg_xy >= abs(y) && gavg_xy <= abs(x)); } else { assert(gavg_xy >= abs(x) && gavg_xy <= abs(y)); @@ -1184,11 +1200,10 @@ contract CryticABDKMath64x64Properties { function gavg_test_operand_order(int128 x, int128 y) public pure { int128 gavg_xy = gavg(x, y); int128 gavg_yx = gavg(y, x); - + assert(gavg_xy == gavg_yx); } - /* ================================================================ Tests for overflow and edge cases. These will make sure that the function reverts on overflow and @@ -1197,41 +1212,34 @@ contract CryticABDKMath64x64Properties { // Test for the maximum value function gavg_test_maximum() public view { - int128 result; + int128 result; // This may revert due to overflow depending on implementation // If it doesn't revert, the result must be MAX_64x64 try this.gavg(MAX_64x64, MAX_64x64) { result = this.gavg(MAX_64x64, MAX_64x64); assert(result == MAX_64x64); - } catch { - - } + } catch {} } // Test for the minimum value function gavg_test_minimum() public view { - int128 result; + int128 result; // This may revert due to overflow depending on implementation // If it doesn't revert, the result must be MIN_64x64 try this.gavg(MIN_64x64, MIN_64x64) { result = this.gavg(MIN_64x64, MIN_64x64); assert(result == MIN_64x64); - } catch { - - } + } catch {} } - - /* ================================================================ TESTS FOR FUNCTION pow() ================================================================ */ - /* ================================================================ Tests for arithmetic properties. These should make sure that the implemented function complies @@ -1274,7 +1282,11 @@ contract CryticABDKMath64x64Properties { // Test for product of powers of the same base // x ** a * x ** b == x ** (a + b) - function pow_test_product_same_base(int128 x, uint256 a, uint256 b) public view { + function pow_test_product_same_base( + int128 x, + uint256 a, + uint256 b + ) public view { require(x != ZERO_FP); int128 x_a = pow(x, a); @@ -1286,7 +1298,11 @@ contract CryticABDKMath64x64Properties { // Test for power of an exponentiation // (x ** a) ** b == x ** (a * b) - function pow_test_power_of_an_exponentiation(int128 x, uint256 a, uint256 b) public view { + function pow_test_power_of_an_exponentiation( + int128 x, + uint256 a, + uint256 b + ) public view { require(x != ZERO_FP); int128 x_a = pow(x, a); @@ -1298,9 +1314,13 @@ contract CryticABDKMath64x64Properties { // Test for power of a product // (x * y) ** a == x ** a * y ** a - function pow_test_product_same_base(int128 x, int128 y, uint256 a) public view { + function pow_test_product_same_base( + int128 x, + int128 y, + uint256 a + ) public view { require(x != ZERO_FP && y != ZERO_FP); - require(a > 2**32); // to avoid massive loss of precision + require(a > 2 ** 32); // to avoid massive loss of precision int128 x_y = mul(x, y); int128 xy_a = pow(x_y, a); @@ -1311,18 +1331,18 @@ contract CryticABDKMath64x64Properties { assert(equal_within_precision(mul(x_a, y_a), xy_a, 2)); } - // Test for result being greater than or lower than the argument, depending on + // Test for result being greater than or lower than the argument, depending on // its absolute value and the value of the exponent function pow_test_values(int128 x, uint256 a) public view { require(x != ZERO_FP); int128 x_a = pow(x, a); - if(abs(x) >= ONE_FP) { + if (abs(x) >= ONE_FP) { assert(abs(x_a) >= ONE_FP); } - if(abs(x) <= ONE_FP) { + if (abs(x) <= ONE_FP) { assert(abs(x_a) <= ONE_FP); } } @@ -1339,7 +1359,7 @@ contract CryticABDKMath64x64Properties { require(x_a != ZERO_FP); // If the exponent is even - if(a % 2 == 0) { + if (a % 2 == 0) { assert(x_a == abs(x_a)); } else { // x_a preserves x sign @@ -1351,7 +1371,6 @@ contract CryticABDKMath64x64Properties { } } - /* ================================================================ Tests for overflow and edge cases. These will make sure that the function reverts on overflow and @@ -1372,15 +1391,13 @@ contract CryticABDKMath64x64Properties { // Test for abs(base) < 1 and high exponent function pow_test_high_exponent(int128 x, uint256 a) public view { - require(abs(x) < ONE_FP && a > 2**64); + require(abs(x) < ONE_FP && a > 2 ** 64); int128 result = pow(x, a); assert(result == ZERO_FP); } - - /* ================================================================ TESTS FOR FUNCTION sqrt() @@ -1402,7 +1419,13 @@ contract CryticABDKMath64x64Properties { int128 sqrt_x_squared = mul(sqrt_x, sqrt_x); // Precision loss is at most half the bits of the operand - assert(equal_within_precision(sqrt_x_squared, x, (toUInt(log_2(x)) >> 1) + 2)); + assert( + equal_within_precision( + sqrt_x_squared, + x, + (toUInt(log_2(x)) >> 1) + 2 + ) + ); } // Test for the inverse operation @@ -1414,7 +1437,13 @@ contract CryticABDKMath64x64Properties { int128 sqrt_x_squared = pow(sqrt_x, 2); // Precision loss is at most half the bits of the operand - assert(equal_within_precision(sqrt_x_squared, x, (toUInt(log_2(x)) >> 1) + 2)); + assert( + equal_within_precision( + sqrt_x_squared, + x, + (toUInt(log_2(x)) >> 1) + 2 + ) + ); } // Test for distributive property respect to the multiplication @@ -1429,7 +1458,10 @@ contract CryticABDKMath64x64Properties { // Ensure we have enough significant digits for the result to be meaningful require(significant_bits_after_mult(x, y) > REQUIRED_SIGNIFICANT_BITS); - require(significant_bits_after_mult(sqrt_x, sqrt_y) > REQUIRED_SIGNIFICANT_BITS); + require( + significant_bits_after_mult(sqrt_x, sqrt_y) > + REQUIRED_SIGNIFICANT_BITS + ); // Allow an error of up to one tenth of a percent assert(equal_within_tolerance(sqrt_x_sqrt_y, sqrt_xy, ONE_TENTH_FP)); @@ -1449,7 +1481,7 @@ contract CryticABDKMath64x64Properties { // Test for maximum value function sqrt_test_maximum() public view { try this.sqrt(MAX_64x64) { - // Expected behaviour, MAX_64x64 is positive, and operation + // Expected behaviour, MAX_64x64 is positive, and operation // should not revert as the result is in range } catch { // Unexpected, should not revert @@ -1479,8 +1511,6 @@ contract CryticABDKMath64x64Properties { } } - - /* ================================================================ TESTS FOR FUNCTION log2() @@ -1507,7 +1537,7 @@ contract CryticABDKMath64x64Properties { require(significant_bits_after_mult(x, y) > REQUIRED_SIGNIFICANT_BITS); // The maximum loss of precision is given by the formula: - // | log_2(x) + log_2(y) | + // | log_2(x) + log_2(y) | uint256 loss = toUInt(abs(log_2(x) + log_2(y))); assert(equal_within_precision(log2_x_log2_y, log2_xy, loss)); @@ -1524,7 +1554,6 @@ contract CryticABDKMath64x64Properties { assert(y_log2_x == toUInt(log2_x_y)); } - /* ================================================================ Tests for overflow and edge cases. These will make sure that the function reverts on overflow and @@ -1567,8 +1596,6 @@ contract CryticABDKMath64x64Properties { } } - - /* ================================================================ TESTS FOR FUNCTION ln() @@ -1596,7 +1623,7 @@ contract CryticABDKMath64x64Properties { require(significant_bits_after_mult(x, y) > REQUIRED_SIGNIFICANT_BITS); // The maximum loss of precision is given by the formula: - // | log_2(x) + log_2(y) | + // | log_2(x) + log_2(y) | uint256 loss = toUInt(abs(log_2(x) + log_2(y))); assert(equal_within_precision(ln_x_ln_y, ln_xy, loss)); @@ -1613,7 +1640,6 @@ contract CryticABDKMath64x64Properties { assert(y_ln_x == toUInt(ln_x_y)); } - /* ================================================================ Tests for overflow and edge cases. These will make sure that the function reverts on overflow and @@ -1656,8 +1682,6 @@ contract CryticABDKMath64x64Properties { } } - - /* ================================================================ TESTS FOR FUNCTION exp2() @@ -1683,11 +1707,11 @@ contract CryticABDKMath64x64Properties { // If y = log_2(x) then exp_2(y) == x function exp2_test_inverse(int128 x) public view { int128 log2_x = log_2(x); - int128 exp2_x = exp_2(log2_x); + int128 exp2_x = exp_2(log2_x); uint256 bits = 50; - if(log2_x < ZERO_FP) { + if (log2_x < ZERO_FP) { bits = uint256(int256(bits) + int256(log2_x)); } @@ -1719,7 +1743,7 @@ contract CryticABDKMath64x64Properties { assert(exp_zero == ONE_FP); } - // Test for maximum value. This should overflow as it won't fit + // Test for maximum value. This should overflow as it won't fit // in the data type function exp2_test_maximum() public view { try this.exp_2(MAX_64x64) { @@ -1729,7 +1753,7 @@ contract CryticABDKMath64x64Properties { // Expected revert } } - + // Test for minimum value. This should return zero since // 2 ** -x == 1 / 2 ** x that tends to zero as x increases function exp2_test_minimum() public view { @@ -1745,8 +1769,6 @@ contract CryticABDKMath64x64Properties { } } - - /* ================================================================ TESTS FOR FUNCTION exp() @@ -1768,7 +1790,7 @@ contract CryticABDKMath64x64Properties { uint256 bits = 48; - if(log2_x < ZERO_FP) { + if (log2_x < ZERO_FP) { bits = uint256(int256(bits) + int256(log2_x)); } @@ -1800,7 +1822,7 @@ contract CryticABDKMath64x64Properties { assert(exp_zero == ONE_FP); } - // Test for maximum value. This should overflow as it won't fit + // Test for maximum value. This should overflow as it won't fit // in the data type function exp_test_maximum() public view { try this.exp(MAX_64x64) { @@ -1810,7 +1832,7 @@ contract CryticABDKMath64x64Properties { // Expected revert } } - + // Test for minimum value. This should return zero since // e ** -x == 1 / e ** x that tends to zero as x increases function exp_test_minimum() public view { @@ -1825,5 +1847,4 @@ contract CryticABDKMath64x64Properties { assert(false); } } - } diff --git a/contracts/Math/ABDKMath64x64/README.md b/contracts/Math/ABDKMath64x64/README.md index f23ccad..dd995b9 100644 --- a/contracts/Math/ABDKMath64x64/README.md +++ b/contracts/Math/ABDKMath64x64/README.md @@ -1,6 +1,7 @@ # ABDKMath64x64 test suite for Echidna ## What is ABDKMath64x64? + The Solidity smart contract programming language does not have any inbuilt feature for working with decimal numbers, so for contracts dealing with non-integer values, a third party solution is needed. ABDKMath64x64 is a fixed-point arithmetic Solidity library that operates on 64.64-bit numbers. This library was developed by [ABDK Consulting](https://abdk.consulting/ "ABDK Consulting") and is [open source](https://github.com/abdk-consulting/abdk-libraries-solidity "open source") under the BSD License. A 64.64-bit fixed-point number is a data type that consists of a sign bit, a 63-bit integer part, and a 64bit decimal part. Since there is no direct support for fractional numbers in the EVM, the underlying data type that stores the values is a 128-bit signed integer. @@ -22,17 +23,18 @@ In principle, these tests are meant to be an entry level practice to learn how t Determining the invariants is a process that involves an intermediate-level comprehension of the library and the math properties behind the operations implemented. For example, the addition function has the `x+y == y+x` commutative property. This statement should always be true, no matter the values of `x` and `y`, therefore it should be a good invariant for the system. More complex operations can demand more complex invariants. The next step, creating the tests, means to implement Solidity functions that verify the previously defined invariants. Echidna is a fuzz tester, so it can quickly test different values for the arguments of a function. For example, the commutative property can be tested using a function that takes two parameters and performs the additions, as shown below: + ```solidity - // Test for commutative property - // x + y == y + x - function add_test_commutative(int128 x, int128 y) public { - int128 x_y = add(x, y); - int128 y_x = add(y, x); - - assert(x_y == y_x); - } +// Test for commutative property +// x + y == y + x +function add_test_commutative(int128 x, int128 y) public { + int128 x_y = add(x, y); + int128 y_x = add(y, x); + + assert(x_y == y_x); +} ``` Finally, the fuzzer has to be instructed to perform the correct type of test, the number of test runs to be made, among other configuration parameters. Since the invariant is checked using an assertion, Echidna must be configured to try to find assertion violations. In this mode, different argument values are passed to `add_test_commutative()`, and the result of the `assert(x_y == y_x)` expression is evaluated for each call: if the assertion is false, the invariant was broken, and it is a sign that there can be an issue with the library implementation. -However, even if this particular test suite is meant as an exercise, it can be used as a template to create tests for other fixed-point arithmetic libraries implementations. \ No newline at end of file +However, even if this particular test suite is meant as an exercise, it can be used as a template to create tests for other fixed-point arithmetic libraries implementations. diff --git a/contracts/util/Hevm.sol b/contracts/util/Hevm.sol index c5f493c..c227580 100644 --- a/contracts/util/Hevm.sol +++ b/contracts/util/Hevm.sol @@ -15,16 +15,21 @@ interface IHevm { function store(address where, bytes32 slot, bytes32 value) external; // Signs data (privateKey, digest) => (r, v, s) - function sign(uint256 privateKey, bytes32 digest) external returns (uint8 r, bytes32 v, bytes32 s); + function sign( + uint256 privateKey, + bytes32 digest + ) external returns (uint8 r, bytes32 v, bytes32 s); // Gets address for a given private key function addr(uint256 privateKey) external returns (address addr); // Performs a foreign function call via terminal - function ffi(string[] calldata inputs) external returns (bytes memory result); - + function ffi( + string[] calldata inputs + ) external returns (bytes memory result); + // Performs the next smart contract call with specified `msg.sender` function prank(address newSender) external; } -IHevm constant hevm = IHevm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); \ No newline at end of file +IHevm constant hevm = IHevm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); diff --git a/contracts/util/IERC20.sol b/contracts/util/IERC20.sol index bd0837a..546b738 100644 --- a/contracts/util/IERC20.sol +++ b/contracts/util/IERC20.sol @@ -1,5 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; + interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to @@ -13,7 +14,11 @@ interface IERC20 { * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ - event Approval(address indexed owner, address indexed spender, uint256 value); + event Approval( + address indexed owner, + address indexed spender, + uint256 value + ); /** * @dev Returns the amount of tokens in existence. @@ -41,7 +46,10 @@ interface IERC20 { * * This value changes when {approve} or {transferFrom} are called. */ - function allowance(address owner, address spender) external view returns (uint256); + function allowance( + address owner, + address spender + ) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. diff --git a/contracts/util/IERC4626.sol b/contracts/util/IERC4626.sol index a870049..9da949b 100644 --- a/contracts/util/IERC4626.sol +++ b/contracts/util/IERC4626.sol @@ -4,7 +4,12 @@ pragma solidity ^0.8.0; import "./IERC20.sol"; interface IERC4626 is IERC20 { - event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares); + event Deposit( + address indexed caller, + address indexed owner, + uint256 assets, + uint256 shares + ); event Withdraw( address indexed caller, @@ -16,9 +21,15 @@ interface IERC4626 is IERC20 { function asset() external view returns (IERC20); - function deposit(uint256 assets, address receiver) external returns (uint256 shares); + function deposit( + uint256 assets, + address receiver + ) external returns (uint256 shares); - function mint(uint256 shares, address receiver) external returns (uint256 assets); + function mint( + uint256 shares, + address receiver + ) external returns (uint256 assets); function withdraw( uint256 assets, @@ -53,4 +64,4 @@ interface IERC4626 is IERC20 { function maxWithdraw(address owner) external view returns (uint256); function maxRedeem(address owner) external view returns (uint256); -} \ No newline at end of file +} diff --git a/contracts/util/PropertiesHelper.sol b/contracts/util/PropertiesHelper.sol index a9519fb..2ee92ce 100644 --- a/contracts/util/PropertiesHelper.sol +++ b/contracts/util/PropertiesHelper.sol @@ -1,7 +1,7 @@ pragma solidity ^0.8.0; abstract contract PropertiesAsserts { - event LogUint256(string,uint256); + event LogUint256(string, uint256); event LogAddress(string, address); event LogString(string); @@ -14,7 +14,7 @@ abstract contract PropertiesAsserts { event AssertLtFail(string); function assertWithMsg(bool b, string memory reason) internal { - if(!b){ + if (!b) { emit AssertFail(reason); assert(false); } @@ -22,10 +22,17 @@ abstract contract PropertiesAsserts { /// @notice asserts that a is equal to b. Violations are logged using reason. function assertEq(uint256 a, uint256 b, string memory reason) internal { - if(a != b){ + if (a != b) { string memory aStr = PropertiesLibString.toString(a); string memory bStr = PropertiesLibString.toString(b); - bytes memory assertMsg = abi.encodePacked("Invalid: ", aStr,"!=",bStr,", reason: ", reason); + bytes memory assertMsg = abi.encodePacked( + "Invalid: ", + aStr, + "!=", + bStr, + ", reason: ", + reason + ); emit AssertEqFail(string(assertMsg)); assert(false); } @@ -33,10 +40,17 @@ abstract contract PropertiesAsserts { /// @notice int256 version of assertEq function assertEq(int256 a, int256 b, string memory reason) internal { - if(a != b){ + if (a != b) { string memory aStr = PropertiesLibString.toString(a); string memory bStr = PropertiesLibString.toString(b); - bytes memory assertMsg = abi.encodePacked("Invalid: ", aStr,"!=",bStr,", reason: ", reason); + bytes memory assertMsg = abi.encodePacked( + "Invalid: ", + aStr, + "!=", + bStr, + ", reason: ", + reason + ); emit AssertEqFail(string(assertMsg)); assert(false); } @@ -44,10 +58,17 @@ abstract contract PropertiesAsserts { /// @notice asserts that a is not equal to b. Violations are logged using reason. function assertNeq(uint256 a, uint256 b, string memory reason) internal { - if(a == b){ + if (a == b) { string memory aStr = PropertiesLibString.toString(a); string memory bStr = PropertiesLibString.toString(b); - bytes memory assertMsg = abi.encodePacked("Invalid: ", aStr,"==",bStr,", reason: ", reason); + bytes memory assertMsg = abi.encodePacked( + "Invalid: ", + aStr, + "==", + bStr, + ", reason: ", + reason + ); emit AssertNeqFail(string(assertMsg)); assert(false); } @@ -55,10 +76,17 @@ abstract contract PropertiesAsserts { /// @notice int256 version of assertNeq function assertNeq(int256 a, int256 b, string memory reason) internal { - if(a == b){ + if (a == b) { string memory aStr = PropertiesLibString.toString(a); string memory bStr = PropertiesLibString.toString(b); - bytes memory assertMsg = abi.encodePacked("Invalid: ", aStr,"==",bStr,", reason: ", reason); + bytes memory assertMsg = abi.encodePacked( + "Invalid: ", + aStr, + "==", + bStr, + ", reason: ", + reason + ); emit AssertNeqFail(string(assertMsg)); assert(false); } @@ -66,10 +94,17 @@ abstract contract PropertiesAsserts { /// @notice asserts that a is greater than or equal to b. Violations are logged using reason. function assertGte(uint256 a, uint256 b, string memory reason) internal { - if(!(a >= b)) { + if (!(a >= b)) { string memory aStr = PropertiesLibString.toString(a); string memory bStr = PropertiesLibString.toString(b); - bytes memory assertMsg = abi.encodePacked("Invalid: ", aStr,"<",bStr," failed, reason: ", reason); + bytes memory assertMsg = abi.encodePacked( + "Invalid: ", + aStr, + "<", + bStr, + " failed, reason: ", + reason + ); emit AssertGteFail(string(assertMsg)); assert(false); } @@ -77,10 +112,17 @@ abstract contract PropertiesAsserts { /// @notice int256 version of assertGte function assertGte(int256 a, int256 b, string memory reason) internal { - if(!(a >= b)) { + if (!(a >= b)) { string memory aStr = PropertiesLibString.toString(a); string memory bStr = PropertiesLibString.toString(b); - bytes memory assertMsg = abi.encodePacked("Invalid: ", aStr,"<",bStr," failed, reason: ", reason); + bytes memory assertMsg = abi.encodePacked( + "Invalid: ", + aStr, + "<", + bStr, + " failed, reason: ", + reason + ); emit AssertGteFail(string(assertMsg)); assert(false); } @@ -88,10 +130,17 @@ abstract contract PropertiesAsserts { /// @notice asserts that a is greater than b. Violations are logged using reason. function assertGt(uint256 a, uint256 b, string memory reason) internal { - if(!(a > b)) { + if (!(a > b)) { string memory aStr = PropertiesLibString.toString(a); string memory bStr = PropertiesLibString.toString(b); - bytes memory assertMsg = abi.encodePacked("Invalid: ", aStr,"<=", bStr," failed, reason: ", reason); + bytes memory assertMsg = abi.encodePacked( + "Invalid: ", + aStr, + "<=", + bStr, + " failed, reason: ", + reason + ); emit AssertGtFail(string(assertMsg)); assert(false); } @@ -99,10 +148,17 @@ abstract contract PropertiesAsserts { /// @notice int256 version of assertGt function assertGt(int256 a, int256 b, string memory reason) internal { - if(!(a > b)) { + if (!(a > b)) { string memory aStr = PropertiesLibString.toString(a); string memory bStr = PropertiesLibString.toString(b); - bytes memory assertMsg = abi.encodePacked("Invalid: ", aStr,"<=", bStr," failed, reason: ", reason); + bytes memory assertMsg = abi.encodePacked( + "Invalid: ", + aStr, + "<=", + bStr, + " failed, reason: ", + reason + ); emit AssertGtFail(string(assertMsg)); assert(false); } @@ -110,10 +166,17 @@ abstract contract PropertiesAsserts { /// @notice asserts that a is less than or equal to b. Violations are logged using reason. function assertLte(uint256 a, uint256 b, string memory reason) internal { - if(!(a <= b)) { + if (!(a <= b)) { string memory aStr = PropertiesLibString.toString(a); string memory bStr = PropertiesLibString.toString(b); - bytes memory assertMsg = abi.encodePacked("Invalid: ", aStr,">", bStr," failed, reason: ", reason); + bytes memory assertMsg = abi.encodePacked( + "Invalid: ", + aStr, + ">", + bStr, + " failed, reason: ", + reason + ); emit AssertLteFail(string(assertMsg)); assert(false); } @@ -121,10 +184,17 @@ abstract contract PropertiesAsserts { /// @notice int256 version of assertLte function assertLte(int256 a, int256 b, string memory reason) internal { - if(!(a <= b)) { + if (!(a <= b)) { string memory aStr = PropertiesLibString.toString(a); string memory bStr = PropertiesLibString.toString(b); - bytes memory assertMsg = abi.encodePacked("Invalid: ", aStr,">", bStr," failed, reason: ", reason); + bytes memory assertMsg = abi.encodePacked( + "Invalid: ", + aStr, + ">", + bStr, + " failed, reason: ", + reason + ); emit AssertLteFail(string(assertMsg)); assert(false); } @@ -132,10 +202,17 @@ abstract contract PropertiesAsserts { /// @notice asserts that a is less than b. Violations are logged using reason. function assertLt(uint256 a, uint256 b, string memory reason) internal { - if(!(a < b)) { + if (!(a < b)) { string memory aStr = PropertiesLibString.toString(a); string memory bStr = PropertiesLibString.toString(b); - bytes memory assertMsg = abi.encodePacked("Invalid: ", aStr,">=",bStr," failed, reason: ", reason); + bytes memory assertMsg = abi.encodePacked( + "Invalid: ", + aStr, + ">=", + bStr, + " failed, reason: ", + reason + ); emit AssertLtFail(string(assertMsg)); assert(false); } @@ -143,22 +220,38 @@ abstract contract PropertiesAsserts { /// @notice int256 version of assertLt function assertLt(int256 a, int256 b, string memory reason) internal { - if(!(a < b)) { + if (!(a < b)) { string memory aStr = PropertiesLibString.toString(a); string memory bStr = PropertiesLibString.toString(b); - bytes memory assertMsg = abi.encodePacked("Invalid: ", aStr,">=",bStr," failed, reason: ", reason); + bytes memory assertMsg = abi.encodePacked( + "Invalid: ", + aStr, + ">=", + bStr, + " failed, reason: ", + reason + ); emit AssertLtFail(string(assertMsg)); assert(false); } } /// @notice Clamps value to be between low and high, both inclusive - function clampBetween(uint256 value, uint256 low, uint256 high) internal returns (uint256) { - if(value < low || value > high) { + function clampBetween( + uint256 value, + uint256 low, + uint256 high + ) internal returns (uint256) { + if (value < low || value > high) { uint ans = low + (value % (high - low + 1)); string memory valueStr = PropertiesLibString.toString(value); string memory ansStr = PropertiesLibString.toString(ans); - bytes memory message = abi.encodePacked("Clamping value ", valueStr, " to ", ansStr); + bytes memory message = abi.encodePacked( + "Clamping value ", + valueStr, + " to ", + ansStr + ); emit LogString(string(message)); return ans; } @@ -166,15 +259,24 @@ abstract contract PropertiesAsserts { } /// @notice int256 version of clampBetween - function clampBetween(int256 value, int256 low, int256 high) internal returns (int256) { - if(value < low || value > high) { + function clampBetween( + int256 value, + int256 low, + int256 high + ) internal returns (int256) { + if (value < low || value > high) { int range = high - low + 1; int clamped = (value - low) % (range); if (clamped < 0) clamped += range; int ans = low + clamped; string memory valueStr = PropertiesLibString.toString(value); string memory ansStr = PropertiesLibString.toString(ans); - bytes memory message = abi.encodePacked("Clamping value ", valueStr, " to ", ansStr); + bytes memory message = abi.encodePacked( + "Clamping value ", + valueStr, + " to ", + ansStr + ); emit LogString(string(message)); return ans; } @@ -182,13 +284,22 @@ abstract contract PropertiesAsserts { } /// @notice clamps a to be less than b - function clampLt(uint256 a, uint256 b) internal returns (uint256){ - if ( !(a < b)) { - assertNeq(b, 0, "clampLt cannot clamp value a to be less than zero. Check your inputs/assumptions."); + function clampLt(uint256 a, uint256 b) internal returns (uint256) { + if (!(a < b)) { + assertNeq( + b, + 0, + "clampLt cannot clamp value a to be less than zero. Check your inputs/assumptions." + ); uint256 value = a % b; string memory aStr = PropertiesLibString.toString(a); string memory valueStr = PropertiesLibString.toString(value); - bytes memory message = abi.encodePacked("Clamping value ", aStr, " to ", valueStr); + bytes memory message = abi.encodePacked( + "Clamping value ", + aStr, + " to ", + valueStr + ); emit LogString(string(message)); return value; } @@ -196,12 +307,17 @@ abstract contract PropertiesAsserts { } /// @notice int256 version of clampLt - function clampLt(int256 a, int256 b) internal returns (int256){ - if ( !(a < b)) { - int256 value = b-1; + function clampLt(int256 a, int256 b) internal returns (int256) { + if (!(a < b)) { + int256 value = b - 1; string memory aStr = PropertiesLibString.toString(a); string memory valueStr = PropertiesLibString.toString(value); - bytes memory message = abi.encodePacked("Clamping value ", aStr, " to ", valueStr); + bytes memory message = abi.encodePacked( + "Clamping value ", + aStr, + " to ", + valueStr + ); emit LogString(string(message)); return value; } @@ -210,11 +326,16 @@ abstract contract PropertiesAsserts { /// @notice clamps a to be less than or equal to b function clampLte(uint256 a, uint256 b) internal returns (uint256) { - if(!(a <= b)) { - uint256 value = a % (b+1); + if (!(a <= b)) { + uint256 value = a % (b + 1); string memory aStr = PropertiesLibString.toString(a); string memory valueStr = PropertiesLibString.toString(value); - bytes memory message = abi.encodePacked("Clamping value ", aStr, " to ", valueStr); + bytes memory message = abi.encodePacked( + "Clamping value ", + aStr, + " to ", + valueStr + ); emit LogString(string(message)); return value; } @@ -223,11 +344,16 @@ abstract contract PropertiesAsserts { /// @notice int256 version of clampLte function clampLte(int256 a, int256 b) internal returns (int256) { - if(!(a <= b)) { + if (!(a <= b)) { int256 value = b; string memory aStr = PropertiesLibString.toString(a); string memory valueStr = PropertiesLibString.toString(value); - bytes memory message = abi.encodePacked("Clamping value ", aStr, " to ", valueStr); + bytes memory message = abi.encodePacked( + "Clamping value ", + aStr, + " to ", + valueStr + ); emit LogString(string(message)); return value; } @@ -236,12 +362,21 @@ abstract contract PropertiesAsserts { /// @notice clamps a to be greater than b function clampGt(uint256 a, uint256 b) internal returns (uint256) { - if(!(a > b)){ - assertNeq(b, type(uint256).max, "clampGt cannot clamp value a to be larger than uint256.max. Check your inputs/assumptions."); - uint256 value = b+1; + if (!(a > b)) { + assertNeq( + b, + type(uint256).max, + "clampGt cannot clamp value a to be larger than uint256.max. Check your inputs/assumptions." + ); + uint256 value = b + 1; string memory aStr = PropertiesLibString.toString(a); string memory valueStr = PropertiesLibString.toString(value); - bytes memory message = abi.encodePacked("Clamping value ", aStr, " to ", valueStr); + bytes memory message = abi.encodePacked( + "Clamping value ", + aStr, + " to ", + valueStr + ); emit LogString(string(message)); return value; } else { @@ -251,11 +386,16 @@ abstract contract PropertiesAsserts { /// @notice int256 version of clampGt function clampGt(int256 a, int256 b) internal returns (int256) { - if(!(a > b)){ - int256 value = b+1; + if (!(a > b)) { + int256 value = b + 1; string memory aStr = PropertiesLibString.toString(a); string memory valueStr = PropertiesLibString.toString(value); - bytes memory message = abi.encodePacked("Clamping value ", aStr, " to ", valueStr); + bytes memory message = abi.encodePacked( + "Clamping value ", + aStr, + " to ", + valueStr + ); emit LogString(string(message)); return value; } else { @@ -265,11 +405,16 @@ abstract contract PropertiesAsserts { /// @notice clamps a to be greater than or equal to b function clampGte(uint256 a, uint256 b) internal returns (uint256) { - if(!(a > b)){ + if (!(a > b)) { uint256 value = b; string memory aStr = PropertiesLibString.toString(a); string memory valueStr = PropertiesLibString.toString(value); - bytes memory message = abi.encodePacked("Clamping value ", aStr, " to ", valueStr); + bytes memory message = abi.encodePacked( + "Clamping value ", + aStr, + " to ", + valueStr + ); emit LogString(string(message)); return value; } @@ -278,11 +423,16 @@ abstract contract PropertiesAsserts { /// @notice int256 version of clampGte function clampGte(int256 a, int256 b) internal returns (int256) { - if(!(a > b)){ + if (!(a > b)) { int256 value = b; string memory aStr = PropertiesLibString.toString(a); string memory valueStr = PropertiesLibString.toString(value); - bytes memory message = abi.encodePacked("Clamping value ", aStr, " to ", valueStr); + bytes memory message = abi.encodePacked( + "Clamping value ", + aStr, + " to ", + valueStr + ); emit LogString(string(message)); return value; } @@ -295,12 +445,11 @@ abstract contract PropertiesAsserts { /// @author Modified from Solady (https://github.com/Vectorized/solady/blob/main/src/utils/LibString.sol) /// @dev Name of the library is modified to prevent collisions with contract-under-test uses of LibString library PropertiesLibString { - function toString(int256 value) internal pure returns (string memory str) { uint256 absValue = value >= 0 ? uint256(value) : uint256(-value); str = toString(absValue); - if(value < 0) { + if (value < 0) { str = string(abi.encodePacked("-", str)); } } @@ -354,14 +503,16 @@ library PropertiesLibString { } } - function toString(address value) internal pure returns (string memory str){ + function toString(address value) internal pure returns (string memory str) { bytes memory s = new bytes(40); for (uint i = 0; i < 20; i++) { - bytes1 b = bytes1(uint8(uint(uint160(value)) / (2**(8*(19 - i))))); + bytes1 b = bytes1( + uint8(uint(uint160(value)) / (2 ** (8 * (19 - i)))) + ); bytes1 hi = bytes1(uint8(b) / 16); bytes1 lo = bytes1(uint8(b) - 16 * uint8(hi)); - s[2*i] = char(hi); - s[2*i+1] = char(lo); + s[2 * i] = char(hi); + s[2 * i + 1] = char(lo); } return string(s); } diff --git a/hardhat.config.js b/hardhat.config.js index ea07066..a443bc8 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -14,20 +14,19 @@ module.exports = { settings: { optimizer: { enabled: true, - runs: 200 - } - } + runs: 200, + }, + }, }, { version: "0.8.17", settings: { optimizer: { enabled: true, - runs: 200 - } - } + runs: 200, + }, + }, }, - ], }, defaultNetwork: "hardhat", diff --git a/package-lock.json b/package-lock.json index e8ffd93..836906c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,9 @@ "license": "MIT", "dependencies": { "@openzeppelin/contracts": "^4.7.3", + "markdown-link-check": "^3.11.0", + "prettier": "^2.8.7", + "prettier-plugin-solidity": "^1.1.3", "solmate": "^6.6.1" }, "devDependencies": { @@ -1171,6 +1174,14 @@ "node": ">=6" } }, + "node_modules/@solidity-parser/parser": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.16.0.tgz", + "integrity": "sha512-ESipEcHyRHg4Np4SqBCfcXwyxxna1DgFVz69bgpLV8vzl/NP1DtcKsJ4dJZXWQhY/Z4J2LeKBiOkOVZn9ct33Q==", + "dependencies": { + "antlr4ts": "^0.5.0-alpha.4" + } + }, "node_modules/@types/async-eventemitter": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/@types/async-eventemitter/-/async-eventemitter-0.2.1.tgz", @@ -1325,6 +1336,11 @@ "node": ">=4" } }, + "node_modules/antlr4ts": { + "version": "0.5.0-alpha.4", + "resolved": "https://registry.npmjs.org/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz", + "integrity": "sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==" + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -1439,6 +1455,11 @@ "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", "dev": true }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -1624,6 +1645,42 @@ "node": ">=4" } }, + "node_modules/cheerio": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", + "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "htmlparser2": "^8.0.1", + "parse5": "^7.0.0", + "parse5-htmlparser2-tree-adapter": "^7.0.0" + }, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -1785,6 +1842,32 @@ "sha.js": "^2.4.8" } }, + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -1832,6 +1915,57 @@ "node": ">=0.3.1" } }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz", + "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.1" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, "node_modules/elliptic": { "version": "6.5.4", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", @@ -1871,6 +2005,17 @@ "node": ">=8.6" } }, + "node_modules/entities": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", + "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/env-paths": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", @@ -2331,6 +2476,32 @@ "minimalistic-crypto-utils": "^1.0.1" } }, + "node_modules/html-link-extractor": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/html-link-extractor/-/html-link-extractor-1.0.5.tgz", + "integrity": "sha512-ADd49pudM157uWHwHQPUSX4ssMsvR/yHIswOR5CUfBdK9g9ZYGMhVSE6KZVHJ6kCkR0gH4htsfzU6zECDNVwyw==", + "dependencies": { + "cheerio": "^1.0.0-rc.10" + } + }, + "node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", @@ -2432,6 +2603,17 @@ "fp-ts": "^1.0.0" } }, + "node_modules/is-absolute-url": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-4.0.1.tgz", + "integrity": "sha512-/51/TKE88Lmm7Gc4/8btclNXWS+g50wXhYJq8HWIBAGUBnoAdRu1aXeh364t/O7wXDAcTJDP8PNuNKWUDWie+A==", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -2525,6 +2707,20 @@ "node": ">=8" } }, + "node_modules/is-relative-url": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-relative-url/-/is-relative-url-4.0.0.tgz", + "integrity": "sha512-PkzoL1qKAYXNFct5IKdKRH/iBQou/oCC85QhXj6WKtUQBliZ4Yfd3Zk27RHu9KQG8r6zgvAA2AQKC9p+rqTszg==", + "dependencies": { + "is-absolute-url": "^4.0.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", @@ -2537,6 +2733,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/isemail": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/isemail/-/isemail-3.2.0.tgz", + "integrity": "sha512-zKqkK+O+dGqevc93KNsbZ/TqTUFd46MwWjYOoMrjIMZ51eU7DtQG3Wmd9SQQT7i7RVnuTPEiYEWHU3MSbxC1Tg==", + "dependencies": { + "punycode": "2.x.x" + }, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/js-sha3": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", @@ -2627,6 +2834,22 @@ "node": ">=12" } }, + "node_modules/link-check": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/link-check/-/link-check-5.2.0.tgz", + "integrity": "sha512-xRbhYLaGDw7eRDTibTAcl6fXtmUQ13vkezQiTqshHHdGueQeumgxxmQMIOmJYsh2p8BF08t8thhDQ++EAOOq3w==", + "dependencies": { + "is-relative-url": "^4.0.0", + "isemail": "^3.2.0", + "ms": "^2.1.3", + "needle": "^3.1.0" + } + }, + "node_modules/link-check/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, "node_modules/locate-path": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", @@ -2643,8 +2866,7 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "node_modules/log-symbols": { "version": "4.1.0", @@ -2747,6 +2969,68 @@ "yallist": "^3.0.2" } }, + "node_modules/markdown-link-check": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/markdown-link-check/-/markdown-link-check-3.11.0.tgz", + "integrity": "sha512-2STxiQ/Np7tE4m1YPHNh/1D22SaH312k7YiGTWna1XdQrh81+42DAw8tRdXIMpJUAcdFbwT9vG8hRyyPzHiKsw==", + "dependencies": { + "async": "^3.2.4", + "chalk": "^5.2.0", + "commander": "^10.0.0", + "link-check": "^5.2.0", + "lodash": "^4.17.21", + "markdown-link-extractor": "^3.1.0", + "needle": "^3.2.0", + "progress": "^2.0.3" + }, + "bin": { + "markdown-link-check": "markdown-link-check" + } + }, + "node_modules/markdown-link-check/node_modules/async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" + }, + "node_modules/markdown-link-check/node_modules/chalk": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", + "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/markdown-link-check/node_modules/commander": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.0.tgz", + "integrity": "sha512-zS5PnTI22FIRM6ylNW8G4Ap0IEOyk62fhLSD0+uHRT9McRCLGpkVNvao4bjimpK/GShynyQkFFxHhwMcETmduA==", + "engines": { + "node": ">=14" + } + }, + "node_modules/markdown-link-extractor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/markdown-link-extractor/-/markdown-link-extractor-3.1.0.tgz", + "integrity": "sha512-r0NEbP1dsM+IqB62Ru9TXLP/HDaTdBNIeylYXumuBi6Xv4ufjE1/g3TnslYL8VNqNcGAGbMptQFHrrdfoZ/Sug==", + "dependencies": { + "html-link-extractor": "^1.0.5", + "marked": "^4.1.0" + } + }, + "node_modules/marked": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 12" + } + }, "node_modules/mcl-wasm": { "version": "0.7.9", "resolved": "https://registry.npmjs.org/mcl-wasm/-/mcl-wasm-0.7.9.tgz", @@ -3017,8 +3301,7 @@ "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/nanoid": { "version": "3.3.3", @@ -3038,6 +3321,41 @@ "integrity": "sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg==", "dev": true }, + "node_modules/needle": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/needle/-/needle-3.2.0.tgz", + "integrity": "sha512-oUvzXnyLiVyVGoianLijF9O/RecZUf7TkBfimjGrLM4eQhXyeJwM6GeAWccwfQ9aa4gMCZKqhAOuLaMIcQxajQ==", + "dependencies": { + "debug": "^3.2.6", + "iconv-lite": "^0.6.3", + "sax": "^1.2.4" + }, + "bin": { + "needle": "bin/needle" + }, + "engines": { + "node": ">= 4.4.x" + } + }, + "node_modules/needle/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/needle/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/node-addon-api": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", @@ -3064,6 +3382,17 @@ "node": ">=0.10.0" } }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, "node_modules/object-inspect": { "version": "1.12.2", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", @@ -3145,6 +3474,29 @@ "node": ">=4" } }, + "node_modules/parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "dependencies": { + "entities": "^4.4.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", + "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", + "dependencies": { + "domhandler": "^5.0.2", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", @@ -3197,6 +3549,82 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/prettier": { + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.7.tgz", + "integrity": "sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==", + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-plugin-solidity": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/prettier-plugin-solidity/-/prettier-plugin-solidity-1.1.3.tgz", + "integrity": "sha512-fQ9yucPi2sBbA2U2Xjh6m4isUTJ7S7QLc/XDDsktqqxYfTwdYKJ0EnnywXHwCGAaYbQNK+HIYPL1OemxuMsgeg==", + "dependencies": { + "@solidity-parser/parser": "^0.16.0", + "semver": "^7.3.8", + "solidity-comments-extractor": "^0.0.7" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "prettier": ">=2.3.0 || >=3.0.0-alpha.0" + } + }, + "node_modules/prettier-plugin-solidity/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prettier-plugin-solidity/node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prettier-plugin-solidity/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "engines": { + "node": ">=6" + } + }, "node_modules/qs": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", @@ -3398,8 +3826,12 @@ "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, "node_modules/scrypt-js": { "version": "3.0.1", @@ -3533,6 +3965,11 @@ "semver": "bin/semver" } }, + "node_modules/solidity-comments-extractor": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/solidity-comments-extractor/-/solidity-comments-extractor-0.0.7.tgz", + "integrity": "sha512-wciNMLg/Irp8OKGrh3S2tfvZiZ0NEyILfcRCXCD4mp7SgK/i9gzLfhY2hY7VMCQJ3kH9UB9BzNdibIVMchzyYw==" + }, "node_modules/solmate": { "version": "6.6.1", "resolved": "https://registry.npmjs.org/solmate/-/solmate-6.6.1.tgz", @@ -4752,6 +5189,14 @@ "tslib": "^1.9.3" } }, + "@solidity-parser/parser": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.16.0.tgz", + "integrity": "sha512-ESipEcHyRHg4Np4SqBCfcXwyxxna1DgFVz69bgpLV8vzl/NP1DtcKsJ4dJZXWQhY/Z4J2LeKBiOkOVZn9ct33Q==", + "requires": { + "antlr4ts": "^0.5.0-alpha.4" + } + }, "@types/async-eventemitter": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/@types/async-eventemitter/-/async-eventemitter-0.2.1.tgz", @@ -4876,6 +5321,11 @@ "color-convert": "^1.9.0" } }, + "antlr4ts": { + "version": "0.5.0-alpha.4", + "resolved": "https://registry.npmjs.org/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz", + "integrity": "sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==" + }, "anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -4964,6 +5414,11 @@ "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", "dev": true }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -5111,6 +5566,33 @@ "supports-color": "^5.3.0" } }, + "cheerio": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", + "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", + "requires": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "htmlparser2": "^8.0.1", + "parse5": "^7.0.0", + "parse5-htmlparser2-tree-adapter": "^7.0.0" + } + }, + "cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "requires": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + } + }, "chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -5245,6 +5727,23 @@ "sha.js": "^2.4.8" } }, + "css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "requires": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + } + }, + "css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==" + }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -5272,6 +5771,39 @@ "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", "dev": true }, + "dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "requires": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + } + }, + "domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" + }, + "domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "requires": { + "domelementtype": "^2.3.0" + } + }, + "domutils": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz", + "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==", + "requires": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.1" + } + }, "elliptic": { "version": "6.5.4", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", @@ -5310,6 +5842,11 @@ "ansi-colors": "^4.1.1" } }, + "entities": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", + "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==" + }, "env-paths": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", @@ -5674,6 +6211,25 @@ "minimalistic-crypto-utils": "^1.0.1" } }, + "html-link-extractor": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/html-link-extractor/-/html-link-extractor-1.0.5.tgz", + "integrity": "sha512-ADd49pudM157uWHwHQPUSX4ssMsvR/yHIswOR5CUfBdK9g9ZYGMhVSE6KZVHJ6kCkR0gH4htsfzU6zECDNVwyw==", + "requires": { + "cheerio": "^1.0.0-rc.10" + } + }, + "htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "requires": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, "http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", @@ -5749,6 +6305,11 @@ "fp-ts": "^1.0.0" } }, + "is-absolute-url": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-4.0.1.tgz", + "integrity": "sha512-/51/TKE88Lmm7Gc4/8btclNXWS+g50wXhYJq8HWIBAGUBnoAdRu1aXeh364t/O7wXDAcTJDP8PNuNKWUDWie+A==" + }, "is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -5803,12 +6364,28 @@ "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "dev": true }, + "is-relative-url": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-relative-url/-/is-relative-url-4.0.0.tgz", + "integrity": "sha512-PkzoL1qKAYXNFct5IKdKRH/iBQou/oCC85QhXj6WKtUQBliZ4Yfd3Zk27RHu9KQG8r6zgvAA2AQKC9p+rqTszg==", + "requires": { + "is-absolute-url": "^4.0.1" + } + }, "is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true }, + "isemail": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/isemail/-/isemail-3.2.0.tgz", + "integrity": "sha512-zKqkK+O+dGqevc93KNsbZ/TqTUFd46MwWjYOoMrjIMZ51eU7DtQG3Wmd9SQQT7i7RVnuTPEiYEWHU3MSbxC1Tg==", + "requires": { + "punycode": "2.x.x" + } + }, "js-sha3": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", @@ -5879,6 +6456,24 @@ "module-error": "^1.0.1" } }, + "link-check": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/link-check/-/link-check-5.2.0.tgz", + "integrity": "sha512-xRbhYLaGDw7eRDTibTAcl6fXtmUQ13vkezQiTqshHHdGueQeumgxxmQMIOmJYsh2p8BF08t8thhDQ++EAOOq3w==", + "requires": { + "is-relative-url": "^4.0.0", + "isemail": "^3.2.0", + "ms": "^2.1.3", + "needle": "^3.1.0" + }, + "dependencies": { + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, "locate-path": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", @@ -5892,8 +6487,7 @@ "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "log-symbols": { "version": "4.1.0", @@ -5971,6 +6565,52 @@ "yallist": "^3.0.2" } }, + "markdown-link-check": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/markdown-link-check/-/markdown-link-check-3.11.0.tgz", + "integrity": "sha512-2STxiQ/Np7tE4m1YPHNh/1D22SaH312k7YiGTWna1XdQrh81+42DAw8tRdXIMpJUAcdFbwT9vG8hRyyPzHiKsw==", + "requires": { + "async": "^3.2.4", + "chalk": "^5.2.0", + "commander": "^10.0.0", + "link-check": "^5.2.0", + "lodash": "^4.17.21", + "markdown-link-extractor": "^3.1.0", + "needle": "^3.2.0", + "progress": "^2.0.3" + }, + "dependencies": { + "async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" + }, + "chalk": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", + "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==" + }, + "commander": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.0.tgz", + "integrity": "sha512-zS5PnTI22FIRM6ylNW8G4Ap0IEOyk62fhLSD0+uHRT9McRCLGpkVNvao4bjimpK/GShynyQkFFxHhwMcETmduA==" + } + } + }, + "markdown-link-extractor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/markdown-link-extractor/-/markdown-link-extractor-3.1.0.tgz", + "integrity": "sha512-r0NEbP1dsM+IqB62Ru9TXLP/HDaTdBNIeylYXumuBi6Xv4ufjE1/g3TnslYL8VNqNcGAGbMptQFHrrdfoZ/Sug==", + "requires": { + "html-link-extractor": "^1.0.5", + "marked": "^4.1.0" + } + }, + "marked": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==" + }, "mcl-wasm": { "version": "0.7.9", "resolved": "https://registry.npmjs.org/mcl-wasm/-/mcl-wasm-0.7.9.tgz", @@ -6169,8 +6809,7 @@ "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "nanoid": { "version": "3.3.3", @@ -6184,6 +6823,34 @@ "integrity": "sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg==", "dev": true }, + "needle": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/needle/-/needle-3.2.0.tgz", + "integrity": "sha512-oUvzXnyLiVyVGoianLijF9O/RecZUf7TkBfimjGrLM4eQhXyeJwM6GeAWccwfQ9aa4gMCZKqhAOuLaMIcQxajQ==", + "requires": { + "debug": "^3.2.6", + "iconv-lite": "^0.6.3", + "sax": "^1.2.4" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "requires": { + "ms": "^2.1.1" + } + }, + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + } + } + }, "node-addon-api": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", @@ -6202,6 +6869,14 @@ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, + "nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "requires": { + "boolbase": "^1.0.0" + } + }, "object-inspect": { "version": "1.12.2", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", @@ -6262,6 +6937,23 @@ "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", "dev": true }, + "parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "requires": { + "entities": "^4.4.0" + } + }, + "parse5-htmlparser2-tree-adapter": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", + "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", + "requires": { + "domhandler": "^5.0.2", + "parse5": "^7.0.0" + } + }, "path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", @@ -6299,6 +6991,54 @@ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, + "prettier": { + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.7.tgz", + "integrity": "sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==" + }, + "prettier-plugin-solidity": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/prettier-plugin-solidity/-/prettier-plugin-solidity-1.1.3.tgz", + "integrity": "sha512-fQ9yucPi2sBbA2U2Xjh6m4isUTJ7S7QLc/XDDsktqqxYfTwdYKJ0EnnywXHwCGAaYbQNK+HIYPL1OemxuMsgeg==", + "requires": { + "@solidity-parser/parser": "^0.16.0", + "semver": "^7.3.8", + "solidity-comments-extractor": "^0.0.7" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==" + }, + "punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==" + }, "qs": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", @@ -6428,8 +7168,12 @@ "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, "scrypt-js": { "version": "3.0.1", @@ -6543,6 +7287,11 @@ } } }, + "solidity-comments-extractor": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/solidity-comments-extractor/-/solidity-comments-extractor-0.0.7.tgz", + "integrity": "sha512-wciNMLg/Irp8OKGrh3S2tfvZiZ0NEyILfcRCXCD4mp7SgK/i9gzLfhY2hY7VMCQJ3kH9UB9BzNdibIVMchzyYw==" + }, "solmate": { "version": "6.6.1", "resolved": "https://registry.npmjs.org/solmate/-/solmate-6.6.1.tgz", diff --git a/package.json b/package.json index 85a8626..330a599 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,12 @@ "main": "index.js", "scripts": { "compile": "hardhat compile", - "test": "echo \"Error: no test specified\" && exit 1" + "test": "echo \"Error: no test specified\" && exit 1", + "format": "prettier --write . && npm run format-embedded-solidity", + "format-embedded-solidity": "prettier --write \"**/*.md\" --embedded-language-formatting=auto --plugin prettier-plugin-solidity --tab-width 4 --print-width 120 && prettier --write \"**/*.md\"", + "lint": "npm run lint-check-format && npm run lint-check-links", + "lint-check-format": "prettier --check .", + "lint-check-links": "find . -name '*.md' -print0 | xargs -0 -n1 markdown-link-check" }, "repository": { "type": "git", @@ -19,6 +24,9 @@ "homepage": "https://github.com/crytic/properties#readme", "dependencies": { "@openzeppelin/contracts": "^4.7.3", + "markdown-link-check": "^3.11.0", + "prettier": "^2.8.7", + "prettier-plugin-solidity": "^1.1.3", "solmate": "^6.6.1" }, "devDependencies": { diff --git a/tests/ERC20/foundry/test/CryticTest.sol b/tests/ERC20/foundry/test/CryticTest.sol index 2178100..9aba67d 100644 --- a/tests/ERC20/foundry/test/CryticTest.sol +++ b/tests/ERC20/foundry/test/CryticTest.sol @@ -2,7 +2,10 @@ pragma solidity ^0.8.0; import "properties/ERC20/internal/properties/ERC20BasicProperties.sol"; import "../src/ExampleToken.sol"; -contract CryticERC20InternalHarness is ExampleToken, CryticERC20BasicProperties { +contract CryticERC20InternalHarness is + ExampleToken, + CryticERC20BasicProperties +{ constructor() { // Setup balances for USER1, USER2 and USER3: _mint(USER1, INITIAL_BALANCE); diff --git a/tests/ERC20/foundry/test/CryticTestExt.sol b/tests/ERC20/foundry/test/CryticTestExt.sol index 84f91ac..18aa5a5 100644 --- a/tests/ERC20/foundry/test/CryticTestExt.sol +++ b/tests/ERC20/foundry/test/CryticTestExt.sol @@ -4,16 +4,13 @@ import {ITokenMock} from "properties/ERC20/external/util/ITokenMock.sol"; import {CryticERC20ExternalBasicProperties} from "properties/ERC20/external/properties/ERC20ExternalBasicProperties.sol"; contract CryticERC20ExternalHarness is CryticERC20ExternalBasicProperties { - constructor() { // Deploy ERC20 token = ITokenMock(address(new TokenMock())); } - } contract TokenMock is ExampleToken { - // Address originating transactions in Echidna (must be equal to the `sender` configuration parameter) address constant USER1 = address(0x10000); address constant USER2 = address(0x20000); @@ -24,7 +21,8 @@ contract TokenMock is ExampleToken { bool public isMintableOrBurnable; uint256 public initialSupply; - constructor () { + + constructor() { _mint(USER1, INITIAL_BALANCE); _mint(USER2, INITIAL_BALANCE); _mint(USER3, INITIAL_BALANCE); @@ -33,5 +31,4 @@ contract TokenMock is ExampleToken { initialSupply = totalSupply(); isMintableOrBurnable = true; } - } diff --git a/tests/ERC20/hardhat/contracts/CryticTest.sol b/tests/ERC20/hardhat/contracts/CryticTest.sol index c32f991..4fb0706 100644 --- a/tests/ERC20/hardhat/contracts/CryticTest.sol +++ b/tests/ERC20/hardhat/contracts/CryticTest.sol @@ -2,7 +2,10 @@ pragma solidity ^0.8.13; import "@crytic/properties/contracts/ERC20/internal/properties/ERC20BasicProperties.sol"; import "./ExampleToken.sol"; -contract CryticERC20InternalHarness is ExampleToken, CryticERC20BasicProperties { +contract CryticERC20InternalHarness is + ExampleToken, + CryticERC20BasicProperties +{ constructor() { // Setup balances for USER1, USER2 and USER3: _mint(USER1, INITIAL_BALANCE); diff --git a/tests/ERC20/hardhat/contracts/CryticTestExt.sol b/tests/ERC20/hardhat/contracts/CryticTestExt.sol index 5a26b5b..bd52c4e 100644 --- a/tests/ERC20/hardhat/contracts/CryticTestExt.sol +++ b/tests/ERC20/hardhat/contracts/CryticTestExt.sol @@ -4,16 +4,13 @@ import {ITokenMock} from "@crytic/properties/contracts/ERC20/external/util/IToke import {CryticERC20ExternalBasicProperties} from "@crytic/properties/contracts/ERC20/external/properties/ERC20ExternalBasicProperties.sol"; contract CryticERC20ExternalHarness is CryticERC20ExternalBasicProperties { - constructor() { // Deploy ERC20 token = ITokenMock(address(new TokenMock())); } - } contract TokenMock is ExampleToken { - // Address originating transactions in Echidna (must be equal to the `sender` configuration parameter) address constant USER1 = address(0x10000); address constant USER2 = address(0x20000); @@ -24,7 +21,8 @@ contract TokenMock is ExampleToken { bool public isMintableOrBurnable; uint256 public initialSupply; - constructor () { + + constructor() { _mint(USER1, INITIAL_BALANCE); _mint(USER2, INITIAL_BALANCE); _mint(USER3, INITIAL_BALANCE); @@ -33,5 +31,4 @@ contract TokenMock is ExampleToken { initialSupply = totalSupply(); isMintableOrBurnable = true; } - } diff --git a/tests/ERC20/hardhat/tests/echidna-config-ext.yaml b/tests/ERC20/hardhat/tests/echidna-config-ext.yaml index 6a65522..f354f62 100644 --- a/tests/ERC20/hardhat/tests/echidna-config-ext.yaml +++ b/tests/ERC20/hardhat/tests/echidna-config-ext.yaml @@ -4,4 +4,3 @@ testLimit: 10000 deployer: "0x10000" sender: ["0x10000", "0x20000", "0x30000"] multi-abi: true - diff --git a/tests/ERC4626/foundry/README.md b/tests/ERC4626/foundry/README.md index 5212ffd..b37c04b 100644 --- a/tests/ERC4626/foundry/README.md +++ b/tests/ERC4626/foundry/README.md @@ -1,12 +1,13 @@ # Crytic Properties Example (Foundry) -Sample repository for a [crytic/properties](https://github.com/crytic/properties) + foundry integration. +Sample repository for a [crytic/properties](https://github.com/crytic/properties) + foundry integration. crytic/properties is a suite of Echidna properties/tests for common interfaces & libraries that can be added to your project to find unique bugs that cannot be easily found with foundry tests. -These properties are designed to be verified using Echidna, and _do not_ use Foundry's built in fuzzer. Running 'forge test' will not execute them. +These properties are designed to be verified using Echidna, and _do not_ use Foundry's built in fuzzer. Running 'forge test' will not execute them. Learn more: + - [crytic/properties](https://github.com/crytic/properties) - [Echidna Fuzzer](https://github.com/crytic/echidna) - [Echidna tutorials](https://secure-contracts.com/program-analysis/echidna/index.html) @@ -23,4 +24,4 @@ Contract under test is `Basic4626Impl`, which inherits from solmate's ERC4626 mi Test harness is `CryticERC4626InternalHarness` -To run tests, use `echidna-test . --contract CryticERC4626InternalHarness --config ./echidna.yaml` \ No newline at end of file +To run tests, use `echidna-test . --contract CryticERC4626InternalHarness --config ./echidna.yaml` diff --git a/tests/ERC4626/foundry/echidna.yaml b/tests/ERC4626/foundry/echidna.yaml index f2c022f..2fc4187 100644 --- a/tests/ERC4626/foundry/echidna.yaml +++ b/tests/ERC4626/foundry/echidna.yaml @@ -1,4 +1,4 @@ -coverage: true +coverage: true corpusDir: "corpus" testMode: assertion testLimit: 10000 diff --git a/tests/ERC4626/foundry/src/Basic4626Impl.sol b/tests/ERC4626/foundry/src/Basic4626Impl.sol index 580b37f..818855d 100644 --- a/tests/ERC4626/foundry/src/Basic4626Impl.sol +++ b/tests/ERC4626/foundry/src/Basic4626Impl.sol @@ -4,19 +4,27 @@ import {ERC20} from "solmate/tokens/ERC20.sol"; import {ERC4626} from "solmate/mixins/ERC4626.sol"; contract Basic4626Impl is ERC4626 { - uint256 private _totalAssets; + uint256 private _totalAssets; - constructor(address _asset) ERC4626(ERC20(_asset), string.concat(ERC20(_asset).name(), " Test Vault"), string.concat(ERC20(_asset).symbol(), "-4626")) {} + constructor( + address _asset + ) + ERC4626( + ERC20(_asset), + string.concat(ERC20(_asset).name(), " Test Vault"), + string.concat(ERC20(_asset).symbol(), "-4626") + ) + {} - function totalAssets() public view virtual override returns (uint256) { - return _totalAssets; - } + function totalAssets() public view virtual override returns (uint256) { + return _totalAssets; + } - function beforeWithdraw(uint256 assets, uint256) internal override { - _totalAssets = _totalAssets - assets; - } + function beforeWithdraw(uint256 assets, uint256) internal override { + _totalAssets = _totalAssets - assets; + } - function afterDeposit(uint256 assets, uint256) internal override { - _totalAssets = _totalAssets + assets; - } -} \ No newline at end of file + function afterDeposit(uint256 assets, uint256) internal override { + _totalAssets = _totalAssets + assets; + } +} diff --git a/tests/ERC4626/foundry/test/CryticTest.sol b/tests/ERC4626/foundry/test/CryticTest.sol index ac1c869..453fbba 100644 --- a/tests/ERC4626/foundry/test/CryticTest.sol +++ b/tests/ERC4626/foundry/test/CryticTest.sol @@ -4,11 +4,10 @@ import {CryticERC4626PropertyTests} from "properties/ERC4626/ERC4626PropertyTest import {TestERC20Token} from "properties/ERC4626/util/TestERC20Token.sol"; import {Basic4626Impl} from "../src/Basic4626Impl.sol"; - -contract CryticERC4626InternalHarness is CryticERC4626PropertyTests{ - constructor () { +contract CryticERC4626InternalHarness is CryticERC4626PropertyTests { + constructor() { TestERC20Token _asset = new TestERC20Token("Test Token", "TT", 18); Basic4626Impl _vault = new Basic4626Impl(address(_asset)); initialize(address(_vault), address(_asset), false); } -} \ No newline at end of file +} diff --git a/tests/ERC4626/hardhat/README.md b/tests/ERC4626/hardhat/README.md index 61a9c25..e05cc18 100644 --- a/tests/ERC4626/hardhat/README.md +++ b/tests/ERC4626/hardhat/README.md @@ -1,12 +1,13 @@ # Crytic Properties Example (Hardhat) -Sample repository for a [crytic/properties](https://github.com/crytic/properties) + hardhat integration. +Sample repository for a [crytic/properties](https://github.com/crytic/properties) + hardhat integration. crytic/properties is a suite of Echidna properties/tests for common interfaces & libraries that can be added to your project to find unique bugs that cannot be easily found with unit tests. -These properties are designed to be verified using Echidna, and _do not_ use Hardhat's testing functionality. Running 'npx hardhat test' will not execute them. +These properties are designed to be verified using Echidna, and _do not_ use Hardhat's testing functionality. Running 'npx hardhat test' will not execute them. Learn more: + - [crytic/properties](https://github.com/crytic/properties) - [Echidna Fuzzer](https://github.com/crytic/echidna) - [Echidna tutorials](https://secure-contracts.com/program-analysis/echidna/index.html) @@ -23,4 +24,4 @@ Contract under test is `Basic4626Impl`, which inherits from solmate's ERC4626 mi Test harness is `Echidna4626Harness` -To run tests, use `npm run echidna4626` or `echidna-test . --contract Echidna4626Harness --config ./echidna.yaml` \ No newline at end of file +To run tests, use `npm run echidna4626` or `echidna-test . --contract Echidna4626Harness --config ./echidna.yaml` diff --git a/tests/ERC4626/hardhat/contracts/Basic4626Impl.sol b/tests/ERC4626/hardhat/contracts/Basic4626Impl.sol index f136427..bf701ae 100644 --- a/tests/ERC4626/hardhat/contracts/Basic4626Impl.sol +++ b/tests/ERC4626/hardhat/contracts/Basic4626Impl.sol @@ -4,19 +4,27 @@ import {ERC20} from "solmate/src/tokens/ERC20.sol"; import {ERC4626} from "solmate/src/mixins/ERC4626.sol"; contract Basic4626Impl is ERC4626 { - uint256 private _totalAssets; + uint256 private _totalAssets; - constructor(address _asset) ERC4626(ERC20(_asset), string.concat(ERC20(_asset).name(), " Test Vault"), string.concat(ERC20(_asset).symbol(), "-4626")) {} + constructor( + address _asset + ) + ERC4626( + ERC20(_asset), + string.concat(ERC20(_asset).name(), " Test Vault"), + string.concat(ERC20(_asset).symbol(), "-4626") + ) + {} - function totalAssets() public view virtual override returns (uint256) { - return _totalAssets; - } + function totalAssets() public view virtual override returns (uint256) { + return _totalAssets; + } - function beforeWithdraw(uint256 assets, uint256) internal override { - _totalAssets = _totalAssets - assets; - } + function beforeWithdraw(uint256 assets, uint256) internal override { + _totalAssets = _totalAssets - assets; + } - function afterDeposit(uint256 assets, uint256) internal override { - _totalAssets = _totalAssets + assets; - } -} \ No newline at end of file + function afterDeposit(uint256 assets, uint256) internal override { + _totalAssets = _totalAssets + assets; + } +} diff --git a/tests/ERC4626/hardhat/contracts/Echidna4626Harness.sol b/tests/ERC4626/hardhat/contracts/Echidna4626Harness.sol index bc034b0..c954eb1 100644 --- a/tests/ERC4626/hardhat/contracts/Echidna4626Harness.sol +++ b/tests/ERC4626/hardhat/contracts/Echidna4626Harness.sol @@ -4,14 +4,12 @@ import {CryticERC4626PropertyTests} from "@crytic/properties/contracts/ERC4626/E import {TestERC20Token} from "@crytic/properties/contracts/ERC4626/util/TestERC20Token.sol"; import "./Basic4626Impl.sol"; -contract CryticERC4626Harness is CryticERC4626PropertyTests{ - constructor () { +contract CryticERC4626Harness is CryticERC4626PropertyTests { + constructor() { TestERC20Token _asset = new TestERC20Token("Test Token", "TT", 18); ERC4626 _vault = new Basic4626Impl(address(_asset)); initialize(address(_vault), address(_asset), false); } - function test(uint256 a) public { - - } + function test(uint256 a) public {} }