Showing preview only (1,585K chars total). Download the full file or copy to clipboard to get everything.
Repository: aave/gho-core
Branch: main
Commit: c6335a0bb9cb
Files: 307
Total size: 1.5 MB
Directory structure:
gitextract_qacr2h46/
├── .github/
│ ├── CODEOWNERS
│ └── workflows/
│ ├── certora-gho-505.yml
│ ├── certora-gho.yml
│ ├── certora-gsm-4626.yml
│ ├── certora-gsm.yml
│ ├── certora-steward.yml
│ ├── dependency-review.yml
│ ├── node.js.yml
│ └── sync-issue.yml
├── .gitignore
├── .gitmodules
├── .husky/
│ ├── .gitignore
│ └── pre-commit
├── .prettierignore
├── .prettierrc
├── .solcover.js
├── .vscode/
│ └── settings.json
├── LICENSE
├── README.md
├── certora/
│ ├── gho/
│ │ ├── Makefile
│ │ ├── applyHarness.patch
│ │ ├── conf/
│ │ │ ├── verifyFlashMinter.conf
│ │ │ ├── verifyGhoAToken.conf
│ │ │ ├── verifyGhoDiscountRateStrategy.conf
│ │ │ ├── verifyGhoToken.conf
│ │ │ ├── verifyGhoVariableDebtToken-rayMulDiv-summarization.conf
│ │ │ ├── verifyGhoVariableDebtToken.conf
│ │ │ ├── verifyGhoVariableDebtTokenInternal.conf
│ │ │ ├── verifyGhoVariableDebtToken_specialBranch.conf
│ │ │ ├── verifyGhoVariableDebtToken_summarized.conf
│ │ │ └── verifyUpgradeableGhoToken.conf
│ │ ├── harness/
│ │ │ ├── DummyERC20A.sol
│ │ │ ├── DummyERC20B.sol
│ │ │ ├── DummyERC20Impl.sol
│ │ │ ├── DummyERC20WithTimedBalanceOf.sol
│ │ │ ├── DummyPool.sol
│ │ │ ├── GhoDiscountRateStrategyHarness.sol
│ │ │ ├── GhoTokenHarness.sol
│ │ │ ├── MockFlashBorrower.sol
│ │ │ ├── UpgradeableGhoTokenHarness.sol
│ │ │ ├── ghoVariableDebtTokenHarness.sol
│ │ │ └── ghoVariableDebtTokenHarnessInternal.sol
│ │ ├── munged/
│ │ │ └── .gitignore
│ │ └── specs/
│ │ ├── VariableDebtToken.spec
│ │ ├── erc20.spec
│ │ ├── flashMinter.spec
│ │ ├── ghoAToken.spec
│ │ ├── ghoDiscountRateStrategy.spec
│ │ ├── ghoToken.spec
│ │ ├── ghoVariableDebtToken-rayMulDiv-summarization.spec
│ │ ├── ghoVariableDebtToken.spec
│ │ ├── ghoVariableDebtTokenInternal.spec
│ │ ├── ghoVariableDebtToken_summarized.spec
│ │ ├── set-natspec.json
│ │ ├── set.spec
│ │ └── summarizations.spec
│ ├── gsm/
│ │ ├── conf/
│ │ │ ├── gsm/
│ │ │ │ ├── FixedFeeStrategy.conf
│ │ │ │ ├── OracleSwapFreezer.conf
│ │ │ │ ├── balances-buy.conf
│ │ │ │ ├── balances-sell.conf
│ │ │ │ ├── fees-buy.conf
│ │ │ │ ├── fees-sell.conf
│ │ │ │ ├── finishedRules.conf
│ │ │ │ ├── getAmount_properties.conf
│ │ │ │ ├── gho-assetToGhoInvertibility.conf
│ │ │ │ ├── gho-fixedPriceStrategy.conf
│ │ │ │ ├── gho-gsm-2.conf
│ │ │ │ ├── gho-gsm.conf
│ │ │ │ ├── gho-gsm_inverse.conf
│ │ │ │ └── optimality.conf
│ │ │ └── gsm4626/
│ │ │ ├── balances-buy-4626.conf
│ │ │ ├── balances-sell-4626.conf
│ │ │ ├── fees-buy-4626.conf
│ │ │ ├── fees-sell-4626.conf
│ │ │ ├── finishedRules4626.conf
│ │ │ ├── getAmount_4626_properties.conf
│ │ │ ├── gho-assetToGhoInvertibility4626.conf
│ │ │ ├── gho-fixedPriceStrategy4626.conf
│ │ │ ├── gho-gsm4626-2.conf
│ │ │ ├── gho-gsm4626.conf
│ │ │ ├── gho-gsm_4626_inverse.conf
│ │ │ └── optimality4626.conf
│ │ ├── harness/
│ │ │ ├── DiffHelper.sol
│ │ │ ├── DummyERC20A.sol
│ │ │ ├── DummyERC20B.sol
│ │ │ ├── DummyERC20Impl.sol
│ │ │ ├── ERC20Helper.sol
│ │ │ ├── FixedFeeStrategyHarness.sol
│ │ │ ├── FixedPriceStrategy4626Harness.sol
│ │ │ ├── FixedPriceStrategyHarness.sol
│ │ │ ├── Gsm4626Harness.sol
│ │ │ ├── GsmHarness.sol
│ │ │ └── OracleSwapFreezerHarness.sol
│ │ ├── munged/
│ │ │ └── .gitignore
│ │ └── specs/
│ │ ├── GsmMethods/
│ │ │ ├── aave_fee_limits.spec
│ │ │ ├── aave_price_fee_limits.spec
│ │ │ ├── aave_price_fee_limits_strict.spec
│ │ │ ├── aave_price_limits.spec
│ │ │ ├── erc20.spec
│ │ │ ├── erc4626.spec
│ │ │ ├── methods4626_base.spec
│ │ │ ├── methods_base-Martin.spec
│ │ │ ├── methods_base.spec
│ │ │ ├── methods_divint_summary.spec
│ │ │ └── shared.spec
│ │ ├── gsm/
│ │ │ ├── AssetToGhoInvertibility.spec
│ │ │ ├── FixedFeeStrategy.spec
│ │ │ ├── FixedPriceStrategy.spec
│ │ │ ├── OracleSwapFreezer.spec
│ │ │ ├── balances-buy.spec
│ │ │ ├── balances-sell.spec
│ │ │ ├── fees-buy.spec
│ │ │ ├── fees-sell.spec
│ │ │ ├── getAmount_properties.spec
│ │ │ ├── gho-gsm-2.spec
│ │ │ ├── gho-gsm-Buy.spec
│ │ │ ├── gho-gsm-finishedRules.spec
│ │ │ ├── gho-gsm.spec
│ │ │ ├── gho-gsm_inverse.spec
│ │ │ └── optimality.spec
│ │ └── gsm4626/
│ │ ├── AssetToGhoInvertibility4626.spec
│ │ ├── FixedPriceStrategy4626.spec
│ │ ├── balances-buy-4626.spec
│ │ ├── balances-sell-4626.spec
│ │ ├── fees-buy-4626.spec
│ │ ├── fees-sell-4626.spec
│ │ ├── getAmount_4626_properties.spec
│ │ ├── gho-gsm-finishedRules4626.spec
│ │ ├── gho-gsm4626-2.spec
│ │ ├── gho-gsm4626.spec
│ │ ├── gho-gsm_4626_inverse.spec
│ │ └── optimality4626.spec
│ └── steward/
│ ├── Makefile
│ ├── applyHarness.patch
│ ├── conf/
│ │ ├── GhoAaveSteward.conf
│ │ ├── GhoBucketSteward.conf
│ │ ├── GhoCcipSteward.conf
│ │ └── GhoGsmSteward.conf
│ ├── harness/
│ │ ├── GhoAaveSteward_Harness.sol
│ │ ├── GhoCcipSteward_Harness.sol
│ │ ├── GhoGsmSteward_Harness.sol
│ │ └── GhoStewardV2_Harness.sol
│ ├── munged/
│ │ └── .gitignore
│ └── specs/
│ ├── GhoAaveSteward.spec
│ ├── GhoBucketSteward.spec
│ ├── GhoCcipSteward.spec
│ └── GhoGsmSteward.spec
├── deploy/
│ ├── 00_deploy_gho_token.ts
│ ├── 01_deploy_gho_oracle.ts
│ ├── 02_deploy_gho_atoken.ts
│ ├── 03_deploy_gho_stable_debt.ts
│ ├── 04_deploy_gho_variable_debt.ts
│ ├── 05_deploy_gho_interest_rate.ts
│ ├── 06_deploy_gho_discount_rate.ts
│ ├── 07_deploy_stakedAave_upgrade.ts
│ ├── 08_deploy_gho_flashminter.ts
│ └── 09_deploy_uighodataprovider.ts
├── docs/
│ └── gho-stewards.md
├── foundry.toml
├── hardhat.config.ts
├── helpers/
│ ├── config.ts
│ ├── constants.ts
│ ├── contract-getters.ts
│ ├── hardhat-config.ts
│ ├── misc-utils.ts
│ └── types.ts
├── package.json
├── remappings.txt
├── setup-test-env.sh
├── slither.config.json
├── src/
│ ├── contracts/
│ │ ├── facilitators/
│ │ │ ├── aave/
│ │ │ │ ├── interestStrategy/
│ │ │ │ │ ├── FixedRateStrategyFactory.sol
│ │ │ │ │ ├── GhoDiscountRateStrategy.sol
│ │ │ │ │ ├── GhoInterestRateStrategy.sol
│ │ │ │ │ ├── ZeroDiscountRateStrategy.sol
│ │ │ │ │ └── interfaces/
│ │ │ │ │ ├── IFixedRateStrategyFactory.sol
│ │ │ │ │ └── IGhoDiscountRateStrategy.sol
│ │ │ │ ├── misc/
│ │ │ │ │ ├── UiGhoDataProvider.sol
│ │ │ │ │ └── interfaces/
│ │ │ │ │ └── IUiGhoDataProvider.sol
│ │ │ │ ├── oracle/
│ │ │ │ │ └── GhoOracle.sol
│ │ │ │ └── tokens/
│ │ │ │ ├── GhoAToken.sol
│ │ │ │ ├── GhoStableDebtToken.sol
│ │ │ │ ├── GhoVariableDebtToken.sol
│ │ │ │ ├── base/
│ │ │ │ │ └── ScaledBalanceTokenBase.sol
│ │ │ │ └── interfaces/
│ │ │ │ ├── IGhoAToken.sol
│ │ │ │ └── IGhoVariableDebtToken.sol
│ │ │ ├── flashMinter/
│ │ │ │ ├── GhoFlashMinter.sol
│ │ │ │ └── interfaces/
│ │ │ │ └── IGhoFlashMinter.sol
│ │ │ └── gsm/
│ │ │ ├── Gsm.sol
│ │ │ ├── Gsm4626.sol
│ │ │ ├── dependencies/
│ │ │ │ └── chainlink/
│ │ │ │ └── AutomationCompatibleInterface.sol
│ │ │ ├── feeStrategy/
│ │ │ │ ├── FixedFeeStrategy.sol
│ │ │ │ ├── FixedFeeStrategyFactory.sol
│ │ │ │ └── interfaces/
│ │ │ │ ├── IFixedFeeStrategyFactory.sol
│ │ │ │ └── IGsmFeeStrategy.sol
│ │ │ ├── interfaces/
│ │ │ │ ├── IGsm.sol
│ │ │ │ └── IGsm4626.sol
│ │ │ ├── misc/
│ │ │ │ ├── GsmRegistry.sol
│ │ │ │ ├── IGsmRegistry.sol
│ │ │ │ ├── SampleLiquidator.sol
│ │ │ │ └── SampleSwapFreezer.sol
│ │ │ ├── priceStrategy/
│ │ │ │ ├── FixedPriceStrategy.sol
│ │ │ │ ├── FixedPriceStrategy4626.sol
│ │ │ │ └── interfaces/
│ │ │ │ └── IGsmPriceStrategy.sol
│ │ │ └── swapFreezer/
│ │ │ └── OracleSwapFreezer.sol
│ │ ├── gho/
│ │ │ ├── ERC20.sol
│ │ │ ├── GhoToken.sol
│ │ │ ├── UpgradeableERC20.sol
│ │ │ ├── UpgradeableGhoToken.sol
│ │ │ └── interfaces/
│ │ │ ├── IGhoFacilitator.sol
│ │ │ └── IGhoToken.sol
│ │ └── misc/
│ │ ├── GhoAaveSteward.sol
│ │ ├── GhoBucketSteward.sol
│ │ ├── GhoCcipSteward.sol
│ │ ├── GhoGsmSteward.sol
│ │ ├── RiskCouncilControlled.sol
│ │ ├── dependencies/
│ │ │ ├── AaveV3-1.sol
│ │ │ └── Ccip.sol
│ │ └── interfaces/
│ │ ├── IGhoAaveSteward.sol
│ │ ├── IGhoBucketSteward.sol
│ │ ├── IGhoCcipSteward.sol
│ │ └── IGhoGsmSteward.sol
│ ├── script/
│ │ ├── DeployGsmLaunch.s.sol
│ │ └── ExternalDependencyCompiler.s.sol
│ └── test/
│ ├── TestFixedRateStrategyFactory.t.sol
│ ├── TestGhoAToken.t.sol
│ ├── TestGhoAaveSteward.t.sol
│ ├── TestGhoBase.t.sol
│ ├── TestGhoBucketSteward.t.sol
│ ├── TestGhoCcipSteward.t.sol
│ ├── TestGhoDiscountRateStrategy.t.sol
│ ├── TestGhoFlashMinter.t.sol
│ ├── TestGhoGsmSteward.t.sol
│ ├── TestGhoInterestRateStrategy.t.sol
│ ├── TestGhoOracle.t.sol
│ ├── TestGhoStableDebtToken.t.sol
│ ├── TestGhoStewardsForkEthereum.t.sol
│ ├── TestGhoStewardsForkRemote.t.sol
│ ├── TestGhoToken.t.sol
│ ├── TestGhoVariableDebtToken.t.sol
│ ├── TestGhoVariableDebtTokenForked.t.sol
│ ├── TestGsm.t.sol
│ ├── TestGsm4626.t.sol
│ ├── TestGsm4626Edge.t.sol
│ ├── TestGsmFixedFeeStrategy.sol
│ ├── TestGsmFixedPriceStrategy.t.sol
│ ├── TestGsmFixedPriceStrategy4626.t.sol
│ ├── TestGsmOracleSwapFreezer.t.sol
│ ├── TestGsmRegistry.t.sol
│ ├── TestGsmSampleLiquidator.t.sol
│ ├── TestGsmSampleSwapFreezer.t.sol
│ ├── TestGsmSwapEdge.t.sol
│ ├── TestGsmSwapFuzz.t.sol
│ ├── TestGsmUpgrade.t.sol
│ ├── TestUiGhoDataProvider.t.sol
│ ├── TestUpgradeableGhoToken.t.sol
│ ├── TestZeroDiscountRateStrategy.t.sol
│ ├── helpers/
│ │ ├── Constants.sol
│ │ ├── DebtUtils.sol
│ │ ├── ErrorsLib.sol
│ │ └── Events.sol
│ └── mocks/
│ ├── MockAclManager.sol
│ ├── MockAddressesProvider.sol
│ ├── MockConfigurator.sol
│ ├── MockERC4626.sol
│ ├── MockFlashBorrower.sol
│ ├── MockGsmV2.sol
│ ├── MockPool.sol
│ ├── MockPoolDataProvider.sol
│ ├── MockUpgradeable.sol
│ ├── MockUpgradeableBurnMintTokenPool.sol
│ └── MockUpgradeableLockReleaseTokenPool.sol
├── tasks/
│ ├── main/
│ │ ├── deploy-and-setup.ts
│ │ └── gho-testnet-setup.ts
│ ├── misc/
│ │ ├── network-check.ts
│ │ └── print-all-deployments.ts
│ ├── roles/
│ │ └── 00_gho-transfer-ownership.ts
│ └── testnet-setup/
│ ├── 00_initialize-gho-reserve.ts
│ ├── 01_enable-gho-borrowing.ts
│ ├── 02_set-gho-oracle.ts
│ ├── 03_add-gho-as-entity.ts
│ ├── 04_add-gho-flashminter-as-entity.ts
│ ├── 05_set-gho-addresses.ts
│ └── 06_upgrade-stkAave.ts
├── test/
│ ├── __setup.test.ts
│ ├── basic-borrow.test.ts
│ ├── borrow-onBehalf.test.ts
│ ├── discount-borrow.test.ts
│ ├── discount-rebalance.test.ts
│ ├── flashmint.test.ts
│ ├── gho-atoken.test.ts
│ ├── gho-oracle.test.ts
│ ├── gho-stable-debt.test.ts
│ ├── gho-token-permit.test.ts
│ ├── gho-token-unit.test.ts
│ ├── gho-variable-debt.test.ts
│ ├── helpers/
│ │ ├── constants.ts
│ │ ├── helpers.ts
│ │ ├── make-suite.ts
│ │ ├── math/
│ │ │ ├── calculations.ts
│ │ │ └── wadraymath.ts
│ │ ├── tokenization-events.ts
│ │ └── user-setup.ts
│ ├── initial-entitiy-configuration.test.ts
│ ├── initial-reserve-configuration.test.ts
│ ├── stkAave-upgrade.test.ts
│ └── transfer-stkAave.test.ts
└── tsconfig.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/CODEOWNERS
================================================
* @miguelmtzinf @foodaka
================================================
FILE: .github/workflows/certora-gho-505.yml
================================================
name: certora-gho-5.0.5
on:
push:
branches:
- main
pull_request:
branches:
- main
workflow_dispatch:
jobs:
verify:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
- name: Install python
uses: actions/setup-python@v5
with: { python-version: 3.9 }
- name: Install java
uses: actions/setup-java@v4
with: { distribution: 'zulu', java-version: '11', java-package: jre }
- name: Install certora cli
run: pip install certora-cli==5.0.5
- name: Install solc
run: |
wget https://github.com/ethereum/solidity/releases/download/v0.8.10/solc-static-linux
chmod +x solc-static-linux
sudo mv solc-static-linux /usr/local/bin/solc8.10
- name: Verify rule ${{ matrix.rule }}
run: |
cd certora/gho
touch applyHarness.patch
make munged
cd ../..
certoraRun certora/gho/conf/${{ matrix.rule }}
env:
CERTORAKEY: ${{ secrets.CERTORAKEY }}
strategy:
fail-fast: false
max-parallel: 16
matrix:
rule:
- verifyGhoVariableDebtToken_specialBranch.conf --rule sendersDiscountPercentCannotIncrease
================================================
FILE: .github/workflows/certora-gho.yml
================================================
name: certora-gho
on:
push:
branches:
- main
pull_request:
branches:
- main
workflow_dispatch:
jobs:
verify:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
- name: Install python
uses: actions/setup-python@v5
with: { python-version: 3.9 }
- name: Install java
uses: actions/setup-java@v4
with: { distribution: 'zulu', java-version: '11', java-package: jre }
- name: Install certora cli
run: pip install certora-cli==4.13.1
- name: Install solc
run: |
wget https://github.com/ethereum/solidity/releases/download/v0.8.10/solc-static-linux
chmod +x solc-static-linux
sudo mv solc-static-linux /usr/local/bin/solc8.10
- name: Verify rule ${{ matrix.rule }}
run: |
cd certora/gho
touch applyHarness.patch
make munged
cd ../..
certoraRun certora/gho/conf/${{ matrix.rule }}
env:
CERTORAKEY: ${{ secrets.CERTORAKEY }}
strategy:
fail-fast: false
max-parallel: 16
matrix:
rule:
- verifyUpgradeableGhoToken.conf
- verifyGhoToken.conf
- verifyGhoAToken.conf --rule noMint noBurn noTransfer transferUnderlyingToCantExceedCapacity totalSupplyAlwaysZero userBalanceAlwaysZero level_does_not_decrease_after_transferUnderlyingTo_followed_by_handleRepayment
- verifyGhoDiscountRateStrategy.conf --rule equivalenceOfWadMulCVLAndWadMulSol maxDiscountForHighDiscountTokenBalance zeroDiscountForSmallDiscountTokenBalance partialDiscountForIntermediateTokenBalance limitOnDiscountRate
- verifyFlashMinter.conf --rule balanceOfFlashMinterGrows integrityOfTreasurySet integrityOfFeeSet availableLiquidityDoesntChange integrityOfDistributeFeesToTreasury feeSimulationEqualsActualFee
- verifyGhoVariableDebtToken.conf --rule user_index_after_mint user_index_ge_one_ray nonzeroNewDiscountToken
- verifyGhoVariableDebtToken.conf --rule accumulated_interest_increase_after_mint
- verifyGhoVariableDebtToken.conf --rule userCantNullifyItsDebt
- verifyGhoVariableDebtToken.conf --rule discountCantExceedDiscountRate
- verifyGhoVariableDebtToken.conf --rule onlyMintForUserCanIncreaseUsersBalance
- verifyGhoVariableDebtToken.conf --rule discountCantExceed100Percent
- verifyGhoVariableDebtToken.conf --rule disallowedFunctionalities nonMintFunctionCantIncreaseBalance nonMintFunctionCantIncreaseScaledBalance debtTokenIsNotTransferable onlyCertainFunctionsCanModifyScaledBalance userAccumulatedDebtInterestWontDecrease integrityOfMint_updateDiscountRate integrityOfMint_updateIndex integrityOfMint_updateScaledBalance_fixedIndex integrityOfMint_userIsolation integrityMint_atoken integrityOfBurn_updateDiscountRate integrityOfBurn_updateIndex burnZeroDoesntChangeBalance integrityOfBurn_fullRepay_concrete integrityOfBurn_userIsolation integrityOfUpdateDiscountDistribution_updateIndex integrityOfUpdateDiscountDistribution_userIsolation integrityOfRebalanceUserDiscountPercent_updateDiscountRate integrityOfRebalanceUserDiscountPercent_updateIndex integrityOfRebalanceUserDiscountPercent_userIsolation integrityOfBalanceOf_fullDiscount integrityOfBalanceOf_noDiscount integrityOfBalanceOf_zeroScaledBalance burnAllDebtReturnsZeroDebt integrityOfUpdateDiscountRateStrategy user_index_up_to_date
- verifyGhoVariableDebtToken_summarized.conf --rule accrueAlwaysCalleldBeforeRefresh
- verifyGhoVariableDebtTokenInternal.conf
- verifyGhoVariableDebtToken-rayMulDiv-summarization.conf
================================================
FILE: .github/workflows/certora-gsm-4626.yml
================================================
name: certora-gsm-4626
on:
push:
branches:
- main
pull_request:
branches:
- main
workflow_dispatch:
jobs:
verify:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
- name: Install python
uses: actions/setup-python@v5
with: { python-version: 3.9 }
- name: Install java
uses: actions/setup-java@v4
with: { distribution: 'zulu', java-version: '11', java-package: jre }
- name: Install certora cli
run: pip install certora-cli==7.14.2
- name: Install solc
run: |
wget https://github.com/ethereum/solidity/releases/download/v0.8.10/solc-static-linux
chmod +x solc-static-linux
sudo mv solc-static-linux /usr/local/bin/solc8.10
- name: Verify rule ${{ matrix.rule }}
run: |
certoraRun certora/gsm/conf/gsm4626/${{ matrix.rule }}
env:
CERTORAKEY: ${{ secrets.CERTORAKEY }}
strategy:
fail-fast: false
max-parallel: 16
matrix:
rule:
- gho-gsm_4626_inverse.conf --rule buySellInverse27 buySellInverse26 buySellInverse25 buySellInverse24 buySellInverse23 buySellInverse22 buySellInverse21 buySellInverse20 buySellInverse19
- gho-gsm4626.conf --rule enoughULtoBackGhoNonBuySell NonZeroFeeCheckSellAsset NonZeroFeeCheckBuyAsset
- balances-buy-4626.conf
- balances-sell-4626.conf --rule R1_getAssetAmountForSellAsset_arg_vs_return R1a_buyGhoUpdatesGhoBalanceCorrectly1 R2_getAssetAmountForSellAsset_sellAsset_eq
- balances-sell-4626.conf --rule R3a_sellAssetUpdatesAssetBalanceCorrectly
- balances-sell-4626.conf --rule R4_buyGhoUpdatesGhoBalanceCorrectly R4a_buyGhoAmountGtGhoBalanceChange
- fees-buy-4626.conf
- fees-sell-4626.conf --rule R3a_estimatedSellFeeCanBeLowerThanActualSellFee R2_getAssetAmountForSellAssetVsActualSellFee R4a_getSellFeeVsgetAssetAmountForSellAsset R4_getSellFeeVsgetAssetAmountForSellAsset R1a_getAssetAmountForSellAssetFeeNeGetSellFee R2a_getAssetAmountForSellAssetNeActualSellFee R4b_getSellFeeVsgetAssetAmountForSellAsset R1_getAssetAmountForSellAssetFeeGeGetSellFee R3b_estimatedSellFeeEqActualSellFee
- gho-gsm4626-2.conf --rule accruedFeesLEGhoBalanceOfThis accruedFeesNeverDecrease systemBalanceStabilitySell systemBalanceStabilitySell
- optimality4626.conf --rule R5a_externalOptimalityOfSellAsset R6a_externalOptimalityOfBuyAsset
- optimality4626.conf --rule R1_optimalityOfBuyAsset_v1
- optimality4626.conf --rule R3_optimalityOfSellAsset_v1
- getAmount_4626_properties.conf --rule getAssetAmountForBuyAsset_correctness_bound1 getAssetAmountForBuyAsset_correctness_bound2 getGhoAmountForBuyAsset_correctness_bound1 getAssetAmountForSellAsset_correctness getAssetAmountForBuyAsset_optimality getAssetAmountForBuyAsset_correctness
- getAmount_4626_properties.conf --rule getGhoAmountForBuyAsset_optimality
- getAmount_4626_properties.conf --rule getGhoAmountForBuyAsset_correctness
- getAmount_4626_properties.conf --rule getAssetAmountForSellAsset_optimality getAssetAmountForBuyAsset_funcProperty
- finishedRules4626.conf --rule cantBuyOrSellWhenSeized cantBuyOrSellWhenFrozen sellAssetIncreasesExposure buyAssetDecreasesExposure rescuingAssetKeepsAccruedFees rescuingGhoKeepsAccruedFees giftingGhoDoesntAffectStorageSIMPLE correctnessOfBuyAsset giftingUnderlyingDoesntAffectStorageSIMPLE sellAssetSameAsGetGhoAmountForSellAsset correctnessOfSellAsset giftingGhoDoesntCreateExcessOrDearth backWithGhoDoesntCreateExcess getAssetAmountForSellAsset_correctness collectedSellFeeIsAtLeastAsRequired collectedBuyFeePlus2IsAtLeastAsRequired collectedBuyFeePlus1IsAtLeastAsRequired collectedBuyFeeIsAtLeastAsRequired sellingDoesntExceedExposureCap whoCanChangeAccruedFees whoCanChangeExposure
- finishedRules4626.conf --rule giftingUnderlyingDoesntCreateExcessOrDearth
================================================
FILE: .github/workflows/certora-gsm.yml
================================================
name: certora-gsm
on:
push:
branches:
- main
pull_request:
branches:
- main
workflow_dispatch:
jobs:
verify:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
- name: Install python
uses: actions/setup-python@v5
with: { python-version: 3.9 }
- name: Install java
uses: actions/setup-java@v4
with: { distribution: 'zulu', java-version: '11', java-package: jre }
- name: Install certora cli
run: pip install certora-cli==6.1.3
- name: Install solc
run: |
wget https://github.com/ethereum/solidity/releases/download/v0.8.10/solc-static-linux
chmod +x solc-static-linux
sudo mv solc-static-linux /usr/local/bin/solc8.10
- name: Verify rule ${{ matrix.rule }}
run: |
certoraRun certora/gsm/conf/gsm/${{ matrix.rule }}
env:
CERTORAKEY: ${{ secrets.CERTORAKEY }}
strategy:
fail-fast: false
max-parallel: 16
matrix:
rule:
- gho-gsm_inverse.conf
- gho-gsm.conf
- balances-buy.conf
- balances-sell.conf
- gho-assetToGhoInvertibility.conf --rule basicProperty_getAssetAmountForBuyAsset sellAssetInverse_all buyAssetInverse_all basicProperty_getGhoAmountForSellAsset basicProperty_getAssetAmountForSellAsset basicProperty_getGhoAmountForBuyAsset
- gho-assetToGhoInvertibility.conf --rule basicProperty2_getAssetAmountForBuyAsset
- gho-fixedPriceStrategy.conf
- fees-buy.conf
- fees-sell.conf
- FixedFeeStrategy.conf
- gho-gsm.conf
- optimality.conf --rule R3_optimalityOfSellAsset_v1 R1_optimalityOfBuyAsset_v1 R6a_externalOptimalityOfBuyAsset R5a_externalOptimalityOfSellAsset R2_optimalityOfBuyAsset_v2
- getAmount_properties.conf --rule getAssetAmountForBuyAsset_funcProperty_LR getAssetAmountForBuyAsset_funcProperty_RL
- finishedRules.conf --rule whoCanChangeExposure whoCanChangeAccruedFees sellingDoesntExceedExposureCap cantBuyOrSellWhenSeized giftingGhoDoesntAffectStorageSIMPLE giftingUnderlyingDoesntAffectStorageSIMPLE collectedBuyFeePlus1IsAtLeastAsRequired sellAssetSameAsGetGhoAmountForSellAsset collectedSellFeeIsAtLeastAsRequired collectedBuyFeeIsAtLeastAsRequired correctnessOfBuyAsset collectedBuyFeePlus2IsAtLeastAsRequired getAssetAmountForSellAsset_correctness cantBuyOrSellWhenFrozen whoCanChangeExposureCap cantSellIfExposureTooHigh sellAssetIncreasesExposure buyAssetDecreasesExposure rescuingGhoKeepsAccruedFees rescuingAssetKeepsAccruedFees
- OracleSwapFreezer.conf
================================================
FILE: .github/workflows/certora-steward.yml
================================================
name: certora-steward
on:
push:
branches:
- main
pull_request:
branches:
- main
workflow_dispatch:
jobs:
verify:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
- name: Install python
uses: actions/setup-python@v5
with: { python-version: 3.9 }
- name: Install java
uses: actions/setup-java@v4
with: { distribution: 'zulu', java-version: '11', java-package: jre }
- name: Install certora cli
run: pip install certora-cli
- name: Install solc
run: |
cd certora/steward/
touch applyHarness.patch
make munged
cd ../..
wget https://github.com/ethereum/solidity/releases/download/v0.8.10/solc-static-linux
chmod +x solc-static-linux
sudo mv solc-static-linux /usr/local/bin/solc8.10
- name: Verify rule ${{ matrix.rule }}
run: |
certoraRun certora/steward/conf/${{ matrix.rule }}
env:
CERTORAKEY: ${{ secrets.CERTORAKEY }}
strategy:
fail-fast: false
max-parallel: 16
matrix:
rule:
- GhoAaveSteward.conf
- GhoBucketSteward.conf
- GhoCcipSteward.conf
- GhoGsmSteward.conf
================================================
FILE: .github/workflows/dependency-review.yml
================================================
name: Dependency Review
on:
- pull_request
permissions:
contents: read
pull-requests: write
jobs:
dependency-review:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v5
- name: Dependency Review
uses: actions/dependency-review-action@v4
with:
comment-summary-in-pr: on-failure
fail-on-severity: moderate
license-check: false
================================================
FILE: .github/workflows/node.js.yml
================================================
name: Build
on: push
jobs:
build:
runs-on:
group: larger
env:
ALCHEMY_KEY: '${{secrets.ALCHEMY_KEY}}'
ETH_RPC_URL: 'https://eth-mainnet.g.alchemy.com/v2/${{secrets.ALCHEMY_KEY}}'
RPC_MAINNET: 'https://eth-mainnet.g.alchemy.com/v2/${{secrets.ALCHEMY_KEY}}'
RPC_ARBITRUM: 'https://arb-mainnet.g.alchemy.com/v2/${{secrets.ALCHEMY_KEY}}'
strategy:
matrix:
node-version:
- 16.x
steps:
- name: Checkout
uses: actions/checkout@v3
with:
submodules: recursive
- name: Setup node
uses: actions/setup-node@v3
with:
node-version: 18.x.x
- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly
- name: Install Dependencies
run: npm ci
- name: Compilation
run: 'npm run compile'
- name: Lint check
run: 'npm run prettier:check'
- name: Test
env:
NODE_OPTIONS: '--max_old_space_size=4096'
run: 'npm run test'
================================================
FILE: .github/workflows/sync-issue.yml
================================================
name: Sync Issue to Height
on:
issues:
types: [opened]
jobs:
sync:
runs-on: ubuntu-latest
steps:
- name: Sync issue
uses: fjogeleit/http-request-action@v1
with:
url: 'https://api.height.app/tasks'
method: 'POST'
customHeaders: '{ "Content-Type": "application/json", "Authorization": "api-key ${{ secrets.HEIGHT_SECRET_TOKEN }}" }'
data: '{ "name": "${{ github.event.issue.title }}", "listIds": ["64fa570a-a252-4521-b801-9b0eb18113bc"], "status": "359d42b4-5e7b-46fa-8465-d179b5a097ef" }'
================================================
FILE: .gitignore
================================================
/cache
/cache_forge
/out
/artifacts
/.env
/node_modules
/deployments
/types
/coverage
/coverage.json
/temp-artifacts
/src/contracts/hardhat-dependency-compiler
.DS_Store
/report
lcov.info
combined-lcov.info
/broadcast
================================================
FILE: .gitmodules
================================================
[submodule "lib/forge-std"]
path = lib/forge-std
url = https://github.com/foundry-rs/forge-std
branch = v1.3.0
[submodule "lib/aave-stk-v1-5"]
path = lib/aave-stk-v1-5
url = https://github.com/bgd-labs/aave-stk-v1-5
[submodule "lib/aave-address-book"]
path = lib/aave-address-book
url = https://github.com/bgd-labs/aave-address-book
[submodule "lib/solidity-utils"]
path = lib/solidity-utils
url = https://github.com/bgd-labs/solidity-utils
[submodule "lib/aave-v3-core"]
path = lib/aave-v3-core
url = https://github.com/aave/aave-v3-core
[submodule "lib/aave-token"]
path = lib/aave-token
url = https://github.com/aave/aave-token
[submodule "lib/aave-v3-periphery"]
path = lib/aave-v3-periphery
url = https://github.com/aave/aave-v3-periphery
[submodule "lib/safety-module"]
path = lib/safety-module
url = https://github.com/aave/safety-module
[submodule "lib/openzeppelin-contracts"]
path = lib/openzeppelin-contracts
url = https://github.com/OpenZeppelin/openzeppelin-contracts
================================================
FILE: .husky/.gitignore
================================================
_
================================================
FILE: .husky/pre-commit
================================================
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx lint-staged
================================================
FILE: .prettierignore
================================================
artifacts
cache
node_modules
types
out
deployments
lib
coverage
cache_forge
================================================
FILE: .prettierrc
================================================
{
"printWidth": 100,
"trailingComma": "es5",
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"overrides": [
{
"files": "*.sol",
"options": {
"semi": true,
"printWidth": 100
}
}
]
}
================================================
FILE: .solcover.js
================================================
module.exports = {
skipFiles: ['./script', './test'],
mocha: {
enableTimeouts: false,
},
configureYulOptimizer: true,
};
================================================
FILE: .vscode/settings.json
================================================
{
"solidity.formatter": "none",
"editor.defaultFormatter": "esbenp.prettier-vscode",
"prettier.configPath": ".prettierrc",
"editor.formatOnSave": true
}
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2023 Aave
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# ⚠️ This repository is DEPRECATED and no longer maintained ⚠️
For the latest GHO code visit the GHO Origin Repository [here](https://github.com/aave-dao/gho-origin).
[](https://github.com/aave/gho/actions/workflows/node.js.yml)
```
.///. .///. //. .// `/////////////-
`++:++` .++:++` :++` `++: `++:......---.`
`/+: -+/` `++- :+/` /+/ `/+/ `++.
/+/ :+/ /+: /+/ `/+/ /+/` `++.
-::/++::` /+: -::/++::` `/+: `++: :++` `++/:::::::::.
-:+++::-` `/+: --++/---` `++- .++- -++. `++/:::::::::.
-++. .++- -++` .++. .++. .++- `++.
.++- -++. .++. -++. -++``++- `++.
`++: :++` .++- :++` :+//+: `++:----------`
-/: :/- -/: :/. ://: `/////////////-
```
# Gho
This repository contains the source code, tests and deployments for both GHO itself and the first facilitator integrating Aave. The repository uses [Hardhat](https://hardhat.org/) development framework.
## Description
GHO is a decentralized, protocol-agnostic crypto-asset intended to maintain a stable value. GHO is minted and burned by approved entities named Facilitators.
The first facilitator is the Aave V3 Ethereum Pool, which allows users to mint GHO against their collateral assets, based on the interest rate set by the Aave Governance. In addition, there is a FlashMint module as a second facilitator, which facilitates arbitrage and liquidations, providing instant liquidity.
Furthermore, the Aave Governance has the ability to approve entities as Facilitators and manage the total amount of GHO they can generate (also known as bucket's capacity).
## Documentation
See the link to the technical paper
- [Technical Paper](./techpaper/GHO_Technical_Paper.pdf)
- [Developer Documentation](https://docs.gho.xyz/)
## Audits and Formal Verification
You can find all audit reports under the [audits](./audits/) folder
- [2022-08-12 - OpenZeppelin](./audits/2022-08-12_Openzeppelin-v1.pdf)
- [2022-11-10 - OpenZeppelin](./audits/2022-11-10_Openzeppelin-v2.pdf)
- [2023-03-01 - ABDK](./audits/2023-03-01_ABDK.pdf)
- [2023-02-28 - Certora Formal Verification](./certora/reports/Aave_Gho_Formal_Verification_Report.pdf)
- [2023-07-06 - Sigma Prime](./audits/2023-07-06_SigmaPrime.pdf)
- [2023-06-13 - Sigma Prime (GhoSteward)](./audits/2023-06-13_GhoSteward_SigmaPrime.pdf)
- [2023-09-20 - Emanuele Ricci @Stermi (GHO Stability Module)](./audits/2023-09-20_GSM_Stermi.pdf)
- [2023-10-23 - Sigma Prime (GHO Stability Module)](./audits/2023-10-23_GSM_SigmaPrime.pdf)
- [2023-12-07 - Certora Formal Verification (GHO Stability Module)](./certora/reports/Formal_Verification_Report_of_GHO_Stability_Module.pdf)
- [2024-03-14 - Certora Formal Verification (GhoStewardV2)](./audits/2024-03-14_GhoStewardV2_Certora.pdf)
- [2024-06-11 - Certora Formal Verification (UpgradeableGHO)](./audits/2024-06-11_UpgradeableGHO_Certora.pdf)
- [2024-06-11 - Certora Formal Verification (Modular Gho Stewards)](./audits/2024-09-15_ModularGhoStewards_Certora.pdf)
## Getting Started
Clone the repository and run the following command to install dependencies:
```sh
npm i
forge i
```
If you need to interact with GHO in the Goerli testnet, provide your Alchemy API key and mnemonic in the `.env` file:
```sh
cp .env.example .env
# Fill ALCHEMY_KEY and MNEMONIC in the .env file with your editor
code .env
```
Compile contracts:
```sh
npm run compile
```
Run the test suite:
```sh
npm run test
```
Deploy and setup GHO in a local Hardhat network:
```sh
npm run deploy-testnet
```
Deploy and setup GHO in Goerli testnet:
```sh
npm run deploy-testnet:goerli
```
## Connect with the community
You can join the [Discord](http://aave.com/discord) channel or the [Governance Forum](https://governance.aave.com/) to ask questions about the protocol or talk about Gho with other peers.
================================================
FILE: certora/gho/Makefile
================================================
default: help
PATCH = applyHarness.patch
CONTRACTS_DIR = ../../src
MUNGED_DIR = munged
help:
@echo "usage:"
@echo " make clean: remove all generated files (those ignored by git)"
@echo " make $(MUNGED_DIR): create $(MUNGED_DIR) directory by applying the patch file to $(CONTRACTS_DIR)"
@echo " make record: record a new patch file capturing the differences between $(CONTRACTS_DIR) and $(MUNGED_DIR)"
munged: $(wildcard $(CONTRACTS_DIR)/*.sol) $(PATCH)
rm -rf $@
cp -r $(CONTRACTS_DIR) $@
patch -p0 -d $@ < $(PATCH)
record:
diff -ruN $(CONTRACTS_DIR) $(MUNGED_DIR) | sed 's+\.\./$(CONTRACTS_DIR)/++g' | sed 's+$(MUNGED_DIR)/++g' > $(PATCH)
clean:
git clean -fdX
touch $(PATCH)
================================================
FILE: certora/gho/applyHarness.patch
================================================
diff -ruN ../../src/contracts/gho/GhoToken.sol contracts/gho/GhoToken.sol
--- ../../src/contracts/gho/GhoToken.sol 2024-05-21 10:57:52.000000000 +0300
+++ contracts/gho/GhoToken.sol 2024-05-27 12:55:24.588859419 +0300
@@ -66,11 +66,16 @@
uint128 bucketCapacity
) external onlyRole(FACILITATOR_MANAGER_ROLE) {
Facilitator storage facilitator = _facilitators[facilitatorAddress];
+ require(
+ !facilitator.isLabelNonempty, //TODO: remove workaroun when CERT-977 is resolved
+ 'FACILITATOR_ALREADY_EXISTS'
+ );
require(bytes(facilitator.label).length == 0, 'FACILITATOR_ALREADY_EXISTS');
require(bytes(facilitatorLabel).length > 0, 'INVALID_LABEL');
facilitator.label = facilitatorLabel;
facilitator.bucketCapacity = bucketCapacity;
+ facilitator.isLabelNonempty = true;
_facilitatorsList.add(facilitatorAddress);
@@ -86,6 +91,10 @@
address facilitatorAddress
) external onlyRole(FACILITATOR_MANAGER_ROLE) {
require(
+ _facilitators[facilitatorAddress].isLabelNonempty, //TODO: remove workaroun when CERT-977 is resolved
+ 'FACILITATOR_DOES_NOT_EXIST'
+ );
+ require(
bytes(_facilitators[facilitatorAddress].label).length > 0,
'FACILITATOR_DOES_NOT_EXIST'
);
@@ -105,6 +114,10 @@
address facilitator,
uint128 newCapacity
) external onlyRole(BUCKET_MANAGER_ROLE) {
+ require(
+ _facilitators[facilitator].isLabelNonempty, //TODO: remove workaroun when CERT-977 is resolved
+ 'FACILITATOR_DOES_NOT_EXIST'
+ );
require(bytes(_facilitators[facilitator].label).length > 0, 'FACILITATOR_DOES_NOT_EXIST');
uint256 oldCapacity = _facilitators[facilitator].bucketCapacity;
@@ -119,12 +132,12 @@
}
/// @inheritdoc IGhoToken
- function getFacilitatorBucket(address facilitator) external view returns (uint256, uint256) {
+ function getFacilitatorBucket(address facilitator) public view returns (uint256, uint256) {
return (_facilitators[facilitator].bucketCapacity, _facilitators[facilitator].bucketLevel);
}
/// @inheritdoc IGhoToken
- function getFacilitatorsList() external view returns (address[] memory) {
+ function getFacilitatorsList() public view returns (address[] memory) {
return _facilitatorsList.values();
}
}
diff -ruN ../../src/contracts/gho/interfaces/IGhoToken.sol contracts/gho/interfaces/IGhoToken.sol
--- ../../src/contracts/gho/interfaces/IGhoToken.sol 2024-05-21 10:57:52.000000000 +0300
+++ contracts/gho/interfaces/IGhoToken.sol 2024-05-27 12:55:24.588859419 +0300
@@ -13,6 +13,7 @@
uint128 bucketCapacity;
uint128 bucketLevel;
string label;
+ bool isLabelNonempty; //TODO: remove workaroun when https://certora.atlassian.net/browse/CERT-977 is resolved
}
/**
diff -ruN ../../src/contracts/gho/UpgradeableGhoToken.sol contracts/gho/UpgradeableGhoToken.sol
--- ../../src/contracts/gho/UpgradeableGhoToken.sol 2024-05-21 11:57:23.000000000 +0300
+++ contracts/gho/UpgradeableGhoToken.sol 2024-05-27 15:04:16.458997293 +0300
@@ -76,11 +76,16 @@
uint128 bucketCapacity
) external onlyRole(FACILITATOR_MANAGER_ROLE) {
Facilitator storage facilitator = _facilitators[facilitatorAddress];
+ require(
+ !facilitator.isLabelNonempty, //TODO: remove workaroun when CERT-977 is resolved
+ 'FACILITATOR_ALREADY_EXISTS'
+ );
require(bytes(facilitator.label).length == 0, 'FACILITATOR_ALREADY_EXISTS');
require(bytes(facilitatorLabel).length > 0, 'INVALID_LABEL');
facilitator.label = facilitatorLabel;
facilitator.bucketCapacity = bucketCapacity;
+ facilitator.isLabelNonempty = true;
_facilitatorsList.add(facilitatorAddress);
@@ -96,6 +101,10 @@
address facilitatorAddress
) external onlyRole(FACILITATOR_MANAGER_ROLE) {
require(
+ _facilitators[facilitatorAddress].isLabelNonempty, //TODO: remove workaroun when CERT-977 is resolved
+ 'FACILITATOR_DOES_NOT_EXIST'
+ );
+ require(
bytes(_facilitators[facilitatorAddress].label).length > 0,
'FACILITATOR_DOES_NOT_EXIST'
);
@@ -115,6 +124,10 @@
address facilitator,
uint128 newCapacity
) external onlyRole(BUCKET_MANAGER_ROLE) {
+ require(
+ _facilitators[facilitator].isLabelNonempty, //TODO: remove workaroun when CERT-977 is resolved
+ 'FACILITATOR_DOES_NOT_EXIST'
+ );
require(bytes(_facilitators[facilitator].label).length > 0, 'FACILITATOR_DOES_NOT_EXIST');
uint256 oldCapacity = _facilitators[facilitator].bucketCapacity;
@@ -129,12 +142,12 @@
}
/// @inheritdoc IGhoToken
- function getFacilitatorBucket(address facilitator) external view returns (uint256, uint256) {
+ function getFacilitatorBucket(address facilitator) public view returns (uint256, uint256) {
return (_facilitators[facilitator].bucketCapacity, _facilitators[facilitator].bucketLevel);
}
/// @inheritdoc IGhoToken
- function getFacilitatorsList() external view returns (address[] memory) {
+ function getFacilitatorsList() public view returns (address[] memory) {
return _facilitatorsList.values();
}
diff -ruN ../../src/.gitignore .gitignore
--- ../../src/.gitignore 1970-01-01 02:00:00.000000000 +0200
+++ .gitignore 2024-05-27 12:55:24.588859419 +0300
@@ -0,0 +1,2 @@
+*
+!.gitignore
================================================
FILE: certora/gho/conf/verifyFlashMinter.conf
================================================
{
"files": [
"certora/gho/munged/contracts/facilitators/flashMinter/GhoFlashMinter.sol:GhoFlashMinter",
"certora/gho/munged/contracts/facilitators/aave/tokens/GhoAToken.sol:GhoAToken",
"certora/gho/munged/contracts/gho/GhoToken.sol",
"certora/gho/harness/MockFlashBorrower.sol"
],
"link": [
"MockFlashBorrower:Gho=GhoToken",
"MockFlashBorrower:AGho=GhoAToken",
"GhoFlashMinter:GHO_TOKEN=GhoToken",
"MockFlashBorrower:minter=GhoFlashMinter"
],
"packages": [
"@aave/core-v3/=lib/aave-v3-core",
"@aave/periphery-v3/=lib/aave-v3-periphery",
"@aave/=lib/aave-token",
"@openzeppelin/=lib/openzeppelin-contracts",
],
"msg": "flashMinter check, all rules",
"optimistic_loop": true,
"process": "emv",
"prover_args": [
" -contractRecursionLimit 1"
],
"solc": "solc8.10",
"verify": "GhoFlashMinter:certora/gho/specs/flashMinter.spec"
}
================================================
FILE: certora/gho/conf/verifyGhoAToken.conf
================================================
{
"files": [
"certora/gho/munged/contracts/facilitators/aave/tokens/GhoAToken.sol",
"certora/gho/munged/contracts/facilitators/aave/tokens/GhoVariableDebtToken.sol",
"certora/gho/munged/contracts/gho/GhoToken.sol",
"certora/gho/harness/GhoTokenHarness.sol",
"certora/gho/harness/DummyERC20A.sol",
"certora/gho/harness/DummyERC20B.sol"
],
"link": [
"GhoAToken:_ghoVariableDebtToken=GhoVariableDebtToken",
"GhoVariableDebtToken:_ghoAToken=GhoAToken",
"GhoAToken:_underlyingAsset=GhoTokenHarness"
],
"packages": [
"@aave/core-v3/=lib/aave-v3-core",
"@aave/periphery-v3/=lib/aave-v3-periphery",
"@aave/=lib/aave-token",
"@openzeppelin/=lib/openzeppelin-contracts",
],
"msg": "GhoAToken, all rules",
"optimistic_loop": true,
"process": "emv",
"solc": "solc8.10",
"verify": "GhoAToken:certora/gho/specs/ghoAToken.spec"
}
================================================
FILE: certora/gho/conf/verifyGhoDiscountRateStrategy.conf
================================================
{
"files": [
"certora/gho/harness/GhoDiscountRateStrategyHarness.sol:GhoDiscountRateStrategyHarness"
],
"packages": [
"@aave/core-v3/=lib/aave-v3-core",
"@aave/periphery-v3/=lib/aave-v3-periphery",
"@aave/=lib/aave-token",
"@openzeppelin/=lib/openzeppelin-contracts",
],
"loop_iter": "2",
"msg": "GhoDiscountRateStrategy, all rules.",
"optimistic_loop": true,
"process": "emv",
"prover_args": [
" -mediumTimeout 20 -depth 10"
],
"smt_timeout": "500",
"solc": "solc8.10",
"verify": "GhoDiscountRateStrategyHarness:certora/gho/specs/ghoDiscountRateStrategy.spec"
}
================================================
FILE: certora/gho/conf/verifyGhoToken.conf
================================================
{
"files": [
"certora/gho/harness/GhoTokenHarness.sol:GhoTokenHarness",
],
"packages": [
"@aave/core-v3/=lib/aave-v3-core",
"@aave/periphery-v3/=lib/aave-v3-periphery",
"@aave/=lib/aave-token",
"@openzeppelin/=lib/openzeppelin-contracts",
],
"loop_iter": "3",
"msg": "GhoToken, all rules.",
"optimistic_loop": true,
"process": "emv",
"solc": "solc8.10",
"verify": "GhoTokenHarness:certora/gho/specs/ghoToken.spec"
}
================================================
FILE: certora/gho/conf/verifyGhoVariableDebtToken-rayMulDiv-summarization.conf
================================================
{
"files": [
"certora/gho/harness/ghoVariableDebtTokenHarness.sol:GhoVariableDebtTokenHarness",
"certora/gho/harness/DummyPool.sol",
"certora/gho/harness/DummyERC20WithTimedBalanceOf.sol",
"certora/gho/munged/contracts/facilitators/aave/interestStrategy/GhoDiscountRateStrategy.sol",
"certora/gho/harness/DummyERC20A.sol",
"certora/gho/harness/DummyERC20B.sol"
],
"link": [
"GhoVariableDebtTokenHarness:_discountToken=DummyERC20WithTimedBalanceOf",
"GhoVariableDebtTokenHarness:POOL=DummyPool",
"GhoVariableDebtTokenHarness:_discountRateStrategy=GhoDiscountRateStrategy"
],
"packages": [
"@aave/core-v3/=lib/aave-v3-core",
"@aave/periphery-v3/=lib/aave-v3-periphery",
"@aave/=lib/aave-token",
"@openzeppelin/=lib/openzeppelin-contracts",
],
"loop_iter": "2",
"msg": " ",
"multi_assert_check": true,
"optimistic_loop": true,
"process": "emv",
"prover_args": [
" -mediumTimeout 30 -depth 15"
],
"smt_timeout": "900",
"solc": "solc8.10",
"verify": "GhoVariableDebtTokenHarness:certora/gho/specs/ghoVariableDebtToken-rayMulDiv-summarization.spec"
}
================================================
FILE: certora/gho/conf/verifyGhoVariableDebtToken.conf
================================================
{
"files": [
"certora/gho/harness/ghoVariableDebtTokenHarness.sol:GhoVariableDebtTokenHarness",
"certora/gho/harness/DummyPool.sol",
"certora/gho/harness/DummyERC20WithTimedBalanceOf.sol",
"certora/gho/munged/contracts/facilitators/aave/interestStrategy/GhoDiscountRateStrategy.sol",
"certora/gho/harness/DummyERC20A.sol",
"certora/gho/harness/DummyERC20B.sol"
],
"link": [
"GhoVariableDebtTokenHarness:_discountRateStrategy=GhoDiscountRateStrategy",
"GhoVariableDebtTokenHarness:POOL=DummyPool",
"GhoVariableDebtTokenHarness:_discountToken=DummyERC20WithTimedBalanceOf"
],
"packages": [
"@aave/core-v3/=lib/aave-v3-core",
"@aave/periphery-v3/=lib/aave-v3-periphery",
"@aave/=lib/aave-token",
"@openzeppelin/=lib/openzeppelin-contracts",
],
"loop_iter": "2",
"msg": "GhoVariableDebtToken",
"optimistic_loop": true,
"process": "emv",
"prover_args": [
" -mediumTimeout 30 -depth 15"
],
"smt_timeout": "900",
"solc": "solc8.10",
"verify": "GhoVariableDebtTokenHarness:certora/gho/specs/ghoVariableDebtToken.spec"
}
================================================
FILE: certora/gho/conf/verifyGhoVariableDebtTokenInternal.conf
================================================
{
"files": [
"certora/gho/harness/ghoVariableDebtTokenHarnessInternal.sol:GhoVariableDebtTokenHarnessInternal",
"certora/gho/munged/contracts/facilitators/aave/interestStrategy/GhoDiscountRateStrategy.sol"
],
"packages": [
"@aave/core-v3/=lib/aave-v3-core",
"@aave/periphery-v3/=lib/aave-v3-periphery",
"@aave/=lib/aave-token",
"@openzeppelin/=lib/openzeppelin-contracts",
],
"loop_iter": "2",
"msg": "GhoVariableDebtToken internal functions",
"optimistic_loop": true,
"process": "emv",
"prover_args": [
" -mediumTimeout 30 -depth 15"
],
"smt_timeout": "900",
"solc": "solc8.10",
"verify": "GhoVariableDebtTokenHarnessInternal:certora/gho/specs/ghoVariableDebtTokenInternal.spec"
}
================================================
FILE: certora/gho/conf/verifyGhoVariableDebtToken_specialBranch.conf
================================================
{
"files": [
"certora/gho/harness/ghoVariableDebtTokenHarness.sol:GhoVariableDebtTokenHarness",
"certora/gho/harness/DummyPool.sol",
"certora/gho/harness/DummyERC20WithTimedBalanceOf.sol",
"certora/gho/munged/contracts/facilitators/aave/interestStrategy/GhoDiscountRateStrategy.sol",
"certora/gho/harness/DummyERC20A.sol",
"certora/gho/harness/DummyERC20B.sol"
],
"link": [
"GhoVariableDebtTokenHarness:POOL=DummyPool",
"GhoVariableDebtTokenHarness:_discountToken=DummyERC20WithTimedBalanceOf",
"GhoVariableDebtTokenHarness:_discountRateStrategy=GhoDiscountRateStrategy"
],
"packages": [
"@aave/core-v3/=lib/aave-v3-core",
"@aave/periphery-v3/=lib/aave-v3-periphery",
"@aave/=lib/aave-token",
"@openzeppelin/=lib/openzeppelin-contracts",
],
"loop_iter": "2",
"msg": "GhoVariableDebtToken",
"optimistic_loop": true,
"process": "emv",
"prover_args": [
" -depth 0 -adaptiveSolverConfig false -smt_nonLinearArithmetic true"
],
"prover_version": "shelly/z3-4-12-3-build",
"solc": "solc8.10",
"verify": "GhoVariableDebtTokenHarness:certora/gho/specs/ghoVariableDebtToken.spec"
}
================================================
FILE: certora/gho/conf/verifyGhoVariableDebtToken_summarized.conf
================================================
{
"files": [
"certora/gho/harness/ghoVariableDebtTokenHarness.sol:GhoVariableDebtTokenHarness",
"certora/gho/harness/DummyPool.sol",
"certora/gho/harness/DummyERC20WithTimedBalanceOf.sol",
"certora/gho/munged/contracts/facilitators/aave/interestStrategy/GhoDiscountRateStrategy.sol",
"certora/gho/harness/DummyERC20A.sol",
"certora/gho/harness/DummyERC20B.sol"
],
"link": [
"GhoVariableDebtTokenHarness:POOL=DummyPool",
"GhoVariableDebtTokenHarness:_discountToken=DummyERC20WithTimedBalanceOf",
"GhoVariableDebtTokenHarness:_discountRateStrategy=GhoDiscountRateStrategy"
],
"packages": [
"@aave/core-v3/=lib/aave-v3-core",
"@aave/periphery-v3/=lib/aave-v3-periphery",
"@aave/=lib/aave-token",
"@openzeppelin/=lib/openzeppelin-contracts",
],
"loop_iter": "2",
"msg": "GhoVariableDebtToken",
"optimistic_loop": true,
"process": "emv",
"prover_args": [
" -mediumTimeout 30 -depth 15"
],
"smt_timeout": "900",
"solc": "solc8.10",
"verify": "GhoVariableDebtTokenHarness:certora/gho/specs/ghoVariableDebtToken_summarized.spec"
}
================================================
FILE: certora/gho/conf/verifyUpgradeableGhoToken.conf
================================================
{
"files": [
"certora/gho/harness/UpgradeableGhoTokenHarness.sol:UpgradeableGhoTokenHarness",
],
"packages": [
"@openzeppelin/=lib/openzeppelin-contracts",
"solidity-utils/=lib/solidity-utils/src/",
],
"loop_iter": "3",
"msg": "GhoToken, all rules.",
"optimistic_loop": true,
"process": "emv",
"solc": "solc8.10",
"verify": "UpgradeableGhoTokenHarness:certora/gho/specs/ghoToken.spec"
}
================================================
FILE: certora/gho/harness/DummyERC20A.sol
================================================
pragma solidity ^0.8.0;
import './DummyERC20Impl.sol';
contract DummyERC20A is DummyERC20Impl {}
================================================
FILE: certora/gho/harness/DummyERC20B.sol
================================================
pragma solidity ^0.8.0;
import './DummyERC20Impl.sol';
contract DummyERC20B is DummyERC20Impl {}
================================================
FILE: certora/gho/harness/DummyERC20Impl.sol
================================================
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.8.0;
// with mint
contract DummyERC20Impl {
uint256 t;
mapping(address => uint256) b;
mapping(address => mapping(address => uint256)) a;
string public name;
string public symbol;
uint public decimals;
function myAddress() public returns (address) {
return address(this);
}
function add(uint a, uint b) internal pure returns (uint256) {
uint c = a + b;
require(c >= a);
return c;
}
function sub(uint a, uint b) internal pure returns (uint256) {
require(a >= b);
return a - b;
}
function totalSupply() external view returns (uint256) {
return t;
}
function balanceOf(address account) external view returns (uint256) {
return b[account];
}
function transfer(address recipient, uint256 amount) external returns (bool) {
b[msg.sender] = sub(b[msg.sender], amount);
b[recipient] = add(b[recipient], amount);
return true;
}
function allowance(address owner, address spender) external view returns (uint256) {
return a[owner][spender];
}
function approve(address spender, uint256 amount) external returns (bool) {
a[msg.sender][spender] = amount;
return true;
}
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool) {
b[sender] = sub(b[sender], amount);
b[recipient] = add(b[recipient], amount);
a[sender][msg.sender] = sub(a[sender][msg.sender], amount);
return true;
}
}
================================================
FILE: certora/gho/harness/DummyERC20WithTimedBalanceOf.sol
================================================
contract DummyERC20WithTimedBalanceOf {
function balanceOf(address user) public view virtual returns (uint256) {
return _balanceOfWithBlockTimestamp(user, block.timestamp);
}
function _balanceOfWithBlockTimestamp(
address user,
uint256 blockTs
) internal view returns (uint256) {
return 0; // STUB! Should be summarized
}
}
================================================
FILE: certora/gho/harness/DummyPool.sol
================================================
contract DummyPool {
function getReserveNormalizedVariableDebt(address asset) external view returns (uint256) {
return _getReserveNormalizedVariableDebtWithBlockTimestamp(asset, block.timestamp);
}
function _getReserveNormalizedVariableDebtWithBlockTimestamp(
address asset,
uint256 blockTs
) internal view returns (uint256) {
return 0; // will be replaced by a summary in the spec file
}
}
================================================
FILE: certora/gho/harness/GhoDiscountRateStrategyHarness.sol
================================================
import {GhoDiscountRateStrategy} from '../munged/contracts/facilitators/aave/interestStrategy/GhoDiscountRateStrategy.sol';
import {WadRayMath} from '@aave/core-v3/contracts/protocol/libraries/math/WadRayMath.sol';
contract GhoDiscountRateStrategyHarness is GhoDiscountRateStrategy {
using WadRayMath for uint256;
function wadMul(uint256 x, uint256 y) external view returns (uint256) {
return x.wadMul(y);
}
}
================================================
FILE: certora/gho/harness/GhoTokenHarness.sol
================================================
pragma solidity ^0.8.0;
import {IGhoToken} from '../munged/contracts/gho/interfaces/IGhoToken.sol';
import '@openzeppelin/contracts/utils/structs/EnumerableSet.sol';
import {GhoToken} from '../munged/contracts/gho/GhoToken.sol';
contract GhoTokenHarness is GhoToken {
using EnumerableSet for EnumerableSet.AddressSet;
constructor() GhoToken(msg.sender) {}
/**
* @notice Returns the bucket capacity
* @param facilitator The address of the facilitator
* @return The facilitator bucket capacity
*/
function getFacilitatorBucketCapacity(address facilitator) public view returns (uint256) {
(uint256 bucketCapacity, ) = getFacilitatorBucket(facilitator);
return bucketCapacity;
}
/**
* @notice Returns the bucket level
* @param facilitator The address of the facilitator
* @return The facilitator bucket level
*/
function getFacilitatorBucketLevel(address facilitator) public view returns (uint256) {
(, uint256 bucketLevel) = getFacilitatorBucket(facilitator);
return bucketLevel;
}
/**
* @notice Returns the length of the facilitator list
* @return The length of the facilitator list
*/
function getFacilitatorsListLen() external view returns (uint256) {
address[] memory flist = getFacilitatorsList();
return flist.length;
}
/**
* @notice Indicator of GhoToken mapping
* @param addr An address of a facilitator
* @return True of facilitator is in GhoToken mapping
*/
function is_in_facilitator_mapping(address addr) external view returns (bool) {
Facilitator memory facilitator = _facilitators[addr];
return facilitator.isLabelNonempty; //TODO: remove workaround when CERT-977 is resolved
// return (bytes(facilitator.label).length > 0);
}
/**
* @notice Indicator of AddressSet mapping
* @param addr An address of a facilitator
* @return True of facilitator is in AddressSet mapping
*/
function is_in_facilitator_set_map(address addr) external view returns (bool) {
return _facilitatorsList.contains(addr);
}
/**
* @notice Indicator of AddressSet list
* @param addr An address of a facilitator
* @return True of facilitator is in AddressSet array
*/
function is_in_facilitator_set_array(address addr) external view returns (bool) {
address[] memory flist = getFacilitatorsList();
for (uint256 i = 0; i < flist.length; ++i) {
if (address(flist[i]) == addr) {
return true;
}
}
return false;
}
/**
* @notice Converts address to bytes32
* @param value Some address
* @return b the value as bytes32
*/
function to_bytes32(address value) external pure returns (bytes32 b) {
b = bytes32(uint256(uint160(value)));
}
}
================================================
FILE: certora/gho/harness/MockFlashBorrower.sol
================================================
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import {IERC3156FlashBorrower} from '@openzeppelin/contracts/interfaces/IERC3156FlashBorrower.sol';
import {IERC3156FlashLender} from '@openzeppelin/contracts/interfaces/IERC3156FlashLender.sol';
import {IGhoFlashMinter} from '../munged/contracts/facilitators/flashMinter/interfaces/IGhoFlashMinter.sol';
import {IGhoToken} from '../munged/contracts/gho/interfaces/IGhoToken.sol';
import {IGhoAToken} from '../munged/contracts/facilitators/aave/tokens/interfaces/IGhoAToken.sol';
contract MockFlashBorrower is IERC3156FlashBorrower {
enum Action {
FLASH_LOAN,
DISTRIBUTE_FEES,
UPDATE_FEES,
UPDATE_TREASURY,
MINT,
BURN,
ADD_FACILITATOR,
REMOVE_FACILITATOR,
SET_FACILITATOR,
APPROVE,
TRANSFER,
TRANSFER_FROM,
TRANSFER_UNDERLYING_TO,
HANDLE_REPAYMENT,
ATOKEN_DISTRIBUTE_FEES,
RESCUE_TOKENS,
SET_VAR_DEBT_TOKEN,
ATOKEN_UPDATE_TREASURY,
OTHER
}
struct Facilitator {
uint128 bucketCapacity;
uint128 bucketLevel;
string label;
}
Action public action;
uint8 public counter;
uint8 public repeat_on_count;
IGhoFlashMinter public minter;
IGhoToken public Gho;
IGhoAToken public AGho;
address public _transferTo;
IERC3156FlashLender private _lender;
bool allowRepayment;
constructor(IERC3156FlashLender lender) {
_lender = lender;
allowRepayment = true;
}
/// @dev ERC-3156 Flash loan callback
function onFlashLoan(
address initiator,
address token,
uint256 amount,
uint256 fee,
bytes calldata data
) external override returns (bytes32) {
require(msg.sender == address(_lender), 'FlashBorrower: Untrusted lender');
require(initiator == address(this), 'FlashBorrower: Untrusted loan initiator');
counter++;
if (action == Action.FLASH_LOAN && counter < repeat_on_count) {
uint256 amount_reenter;
bytes calldata data_reenter;
minter.flashLoan(IERC3156FlashBorrower(address(this)), token, amount, data);
} else if (action == Action.DISTRIBUTE_FEES) {
minter.distributeFeesToTreasury();
} else if (action == Action.UPDATE_FEES) {
uint256 new_fee;
minter.updateFee(new_fee);
} else if (action == Action.UPDATE_TREASURY) {
address newGhoTreasury;
minter.updateGhoTreasury(newGhoTreasury);
} else if (action == Action.MINT) {
address account;
uint256 amt;
Gho.mint(account, amt);
} else if (action == Action.BURN) {
uint256 amt;
Gho.burn(amt);
// } else if (action == Action.ADD_FACILITATOR) {
// address facilitatorAddress; Facilitator memory facilitatorConfig;
// Gho.addFacilitator(facilitatorAddress, facilitatorConfig);
} else if (action == Action.REMOVE_FACILITATOR) {
address facilitatorAddress;
Gho.removeFacilitator(facilitatorAddress);
} else if (action == Action.SET_FACILITATOR) {
address facilitator;
uint128 newCapacity;
Gho.setFacilitatorBucketCapacity(facilitator, newCapacity);
} else if (action == Action.APPROVE) {
address spender;
uint256 amt;
AGho.approve(spender, amt);
} else if (action == Action.TRANSFER) {
uint256 amt;
AGho.transfer(_transferTo, amt);
} else if (action == Action.TRANSFER_FROM) {
address from;
uint256 amt;
AGho.transferFrom(from, _transferTo, amt);
} else if (action == Action.TRANSFER_UNDERLYING_TO) {
address target;
uint256 amt;
AGho.transferUnderlyingTo(target, amt);
} else if (action == Action.HANDLE_REPAYMENT) {
address user;
address onBehalfOf;
uint256 amt;
AGho.handleRepayment(user, onBehalfOf, amt);
} else if (action == Action.ATOKEN_DISTRIBUTE_FEES) {
AGho.distributeFeesToTreasury();
} else if (action == Action.RESCUE_TOKENS) {
address token;
address to;
uint256 amt;
AGho.rescueTokens(token, to, amt);
} else if (action == Action.SET_VAR_DEBT_TOKEN) {
address ghoVariableDebtToken;
AGho.setVariableDebtToken(ghoVariableDebtToken);
} else if (action == Action.ATOKEN_UPDATE_TREASURY) {
address newGhoTreasury;
AGho.updateGhoTreasury(newGhoTreasury);
} else if (action == Action.OTHER) {
require(true);
}
return keccak256('ERC3156FlashBorrower.onFlashLoan');
}
/// @dev Initiate a flash loan
function flashBorrow(address token, uint256 amount) public {
bytes memory data = abi.encode(Action.FLASH_LOAN);
if (allowRepayment) {
uint256 allowance = IERC20(token).allowance(address(this), address(_lender));
uint256 fee = _lender.flashFee(token, amount);
uint256 repayment = amount + fee;
IERC20(token).approve(address(_lender), allowance + repayment);
}
_lender.flashLoan(this, token, amount, data);
}
function setAllowRepayment(bool active) public {
allowRepayment = active;
}
}
================================================
FILE: certora/gho/harness/UpgradeableGhoTokenHarness.sol
================================================
pragma solidity ^0.8.0;
import {IGhoToken} from '../munged/contracts/gho/interfaces/IGhoToken.sol';
import '@openzeppelin/contracts/utils/structs/EnumerableSet.sol';
import {UpgradeableGhoToken} from '../munged/contracts/gho/UpgradeableGhoToken.sol';
contract UpgradeableGhoTokenHarness is UpgradeableGhoToken {
using EnumerableSet for EnumerableSet.AddressSet;
constructor() UpgradeableGhoToken() {}
/**
* @notice Returns the bucket capacity
* @param facilitator The address of the facilitator
* @return The facilitator bucket capacity
*/
function getFacilitatorBucketCapacity(address facilitator) public view returns (uint256) {
(uint256 bucketCapacity, ) = getFacilitatorBucket(facilitator);
return bucketCapacity;
}
/**
* @notice Returns the bucket level
* @param facilitator The address of the facilitator
* @return The facilitator bucket level
*/
function getFacilitatorBucketLevel(address facilitator) public view returns (uint256) {
(, uint256 bucketLevel) = getFacilitatorBucket(facilitator);
return bucketLevel;
}
/**
* @notice Returns the length of the facilitator list
* @return The length of the facilitator list
*/
function getFacilitatorsListLen() external view returns (uint256) {
address[] memory flist = getFacilitatorsList();
return flist.length;
}
/**
* @notice Indicator of GhoToken mapping
* @param addr An address of a facilitator
* @return True of facilitator is in GhoToken mapping
*/
function is_in_facilitator_mapping(address addr) external view returns (bool) {
Facilitator memory facilitator = _facilitators[addr];
return facilitator.isLabelNonempty; //TODO: remove workaround when CERT-977 is resolved
// return (bytes(facilitator.label).length > 0);
}
/**
* @notice Indicator of AddressSet mapping
* @param addr An address of a facilitator
* @return True of facilitator is in AddressSet mapping
*/
function is_in_facilitator_set_map(address addr) external view returns (bool) {
return _facilitatorsList.contains(addr);
}
/**
* @notice Indicator of AddressSet list
* @param addr An address of a facilitator
* @return True of facilitator is in AddressSet array
*/
function is_in_facilitator_set_array(address addr) external view returns (bool) {
address[] memory flist = getFacilitatorsList();
for (uint256 i = 0; i < flist.length; ++i) {
if (address(flist[i]) == addr) {
return true;
}
}
return false;
}
/**
* @notice Converts address to bytes32
* @param value Some address
* @return b the value as bytes32
*/
function to_bytes32(address value) external pure returns (bytes32 b) {
b = bytes32(uint256(uint160(value)));
}
}
================================================
FILE: certora/gho/harness/ghoVariableDebtTokenHarness.sol
================================================
pragma solidity 0.8.10;
import {GhoVariableDebtToken} from '../munged/contracts/facilitators/aave/tokens/GhoVariableDebtToken.sol';
import {WadRayMath} from '@aave/core-v3/contracts/protocol/libraries/math/WadRayMath.sol';
import {IPool} from '@aave/core-v3/contracts/interfaces/IPool.sol';
contract GhoVariableDebtTokenHarness is GhoVariableDebtToken {
using WadRayMath for uint256;
constructor(IPool pool) public GhoVariableDebtToken(pool) {
//nop
}
function getUserCurrentIndex(address user) external view returns (uint256) {
return _userState[user].additionalData;
}
function getUserDiscountRate(address user) external view returns (uint256) {
return _ghoUserState[user].discountPercent;
}
function getUserAccumulatedDebtInterest(address user) external view returns (uint256) {
return _ghoUserState[user].accumulatedDebtInterest;
}
function scaledBalanceOfToBalanceOf(uint256 bal) public view returns (uint256) {
return bal.rayMul(POOL.getReserveNormalizedVariableDebt(_underlyingAsset));
}
function getBalanceOfDiscountToken(address user) external returns (uint256) {
return _discountToken.balanceOf(user);
}
function rayMul(uint256 x, uint256 y) external view returns (uint256) {
return x.rayMul(y);
}
function rayDiv(uint256 x, uint256 y) external view returns (uint256) {
return x.rayDiv(y);
}
function get_ghoAToken() external returns (address) {
return _ghoAToken;
}
}
================================================
FILE: certora/gho/harness/ghoVariableDebtTokenHarnessInternal.sol
================================================
pragma solidity 0.8.10;
import {GhoVariableDebtTokenHarness} from './ghoVariableDebtTokenHarness.sol';
import {GhoVariableDebtToken} from '../munged/contracts/facilitators/aave/tokens/GhoVariableDebtToken.sol';
import {IPool} from '@aave/core-v3/contracts/interfaces/IPool.sol';
contract GhoVariableDebtTokenHarnessInternal is GhoVariableDebtTokenHarness {
constructor(IPool pool) public GhoVariableDebtTokenHarness(pool) {
//nop
}
function accrueDebtOnAction(
address user,
uint256 previousScaledBalance,
uint256 discountPercent,
uint256 index
) external returns (uint256, uint256) {
return _accrueDebtOnAction(user, previousScaledBalance, discountPercent, index);
}
}
================================================
FILE: certora/gho/munged/.gitignore
================================================
*
!.gitignore
================================================
FILE: certora/gho/specs/VariableDebtToken.spec
================================================
methods {
// summarization for elimination the raymul operation in balance of and totalSupply.
//getReserveNormalizedVariableDebt(address asset) returns (uint256) => indexAtTimestamp(e.block.timestamp)
//setAdditionalData(address user, uint128 data) envfree
function _.handleAction(address, uint256, uint256) external => NONDET;
function scaledBalanceOfToBalanceOf(uint256) external returns (uint256) envfree;
//balanceOf(address) returns (uint256) envfree
}
definition ray() returns uint256 = 1000000000000000000000000000; // 10^27
definition wad() returns uint256 = 1000000000000000000; // 10^18
definition bound(uint256 index) returns mathint = ((index / ray()) + 1 ) / 2;
// summarization for scaledBalanaceOf -> regularBalanceOf + 0.5 (canceling the rayMul)
// ghost gRNVB() returns uint256 {
// axiom gRNVB() == 7 * ray();
// }
/*
Due to rayDiv and RayMul Rounding (+ 0.5) - balance could increase by (gRNI() / Ray() + 1) / 2.
*/
definition bounded_error_eq(uint x, uint y, uint scale, uint256 index) returns bool = to_mathint(x) <= y + (bound(index) * scale) && x + (bound(index) * scale) >= to_mathint(y);
definition disAllowedFunctions(method f) returns bool =
f.selector == sig:transfer(address, uint256).selector ||
f.selector == sig:allowance(address, address).selector ||
f.selector == sig:approve(address, uint256).selector ||
f.selector == sig:transferFrom(address, address, uint256).selector ||
f.selector == sig:increaseAllowance(address, uint256).selector ||
f.selector == sig:decreaseAllowance(address, uint256).selector;
ghost sumAllBalance() returns mathint {
init_state axiom sumAllBalance() == 0;
}
hook Sstore _userState[KEY address a].balance uint128 balance (uint128 old_balance) STORAGE {
havoc sumAllBalance assuming sumAllBalance@new() == sumAllBalance@old() + balance - old_balance;
}
invariant totalSupplyEqualsSumAllBalance(env e)
totalSupply() == scaledBalanceOfToBalanceOf(sumAllBalance())
filtered { f -> !f.isView && !disAllowedFunctions(f) }
{
preserved mint(address user, address onBehalfOf, uint256 amount, uint256 index) with (env e2) {
require index == indexAtTimestamp(e.block.timestamp);
}
preserved burn(address from, uint256 amount, uint256 index) with (env e3) {
require index == indexAtTimestamp(e.block.timestamp);
}
}
// Only the pool with burn or mint operation can change the total supply. (assuming the getReserveNormalizedVariableDebt is not changed)
rule whoChangeTotalSupply(method f)
filtered { f -> !f.isView && !disAllowedFunctions(f) }
{
env e;
uint256 oldTotalSupply = totalSupply();
calldataarg args;
f(e, args);
uint256 newTotalSupply = totalSupply();
assert oldTotalSupply != newTotalSupply =>
(e.msg.sender == POOL(e) &&
(f.selector == sig:burn(address, uint256, uint256).selector ||
f.selector == sig:mint(address, address, uint256, uint256).selector));
}
/*
Each operation of Variable Debt Token can change at most one user's balance.
*/
rule balanceOfChange(address a, address b, method f)
filtered { f -> !f.isView && !disAllowedFunctions(f) }
{
env e;
require a != b;
uint256 balanceABefore = balanceOf(e, a);
uint256 balanceBBefore = balanceOf(e, b);
calldataarg arg;
f(e, arg);
uint256 balanceAAfter = balanceOf(e, a);
uint256 balanceBAfter = balanceOf(e, b);
assert (balanceABefore == balanceAAfter || balanceBBefore == balanceBAfter);
}
/*
Each operation of Variable Debt Token can change at most two user's balance.
*/
rule balanceOfAtMost3Change(address a, address b, address c, method f)
filtered { f -> !f.isView && !disAllowedFunctions(f) }
{
env e;
require a != b;
require a != c;
require b != c;
uint256 balanceABefore = balanceOf(e, a);
uint256 balanceBBefore = balanceOf(e, b);
uint256 balanceCBefore = balanceOf(e, c);
calldataarg arg;
f(e, arg);
uint256 balanceAAfter = balanceOf(e, a);
uint256 balanceBAfter = balanceOf(e, b);
uint256 balanceCAfter = balanceOf(e, c);
assert !(balanceABefore != balanceAAfter && balanceBBefore != balanceBAfter && balanceCBefore != balanceCAfter);
}
// only delegationWithSig operation can change the nonce.
rule nonceChangePermits(method f)
filtered { f -> !f.isView && !disAllowedFunctions(f) }
{
env e;
address user;
uint256 oldNonce = nonces(e, user);
calldataarg args;
f(e, args);
uint256 newNonce = nonces(e, user);
assert oldNonce != newNonce => f.selector == sig:delegationWithSig(address, address, uint256, uint256, uint8, bytes32, bytes32).selector;
}
// minting and then burning Variable Debt Token should have no effect on the users balance
rule inverseMintBurn(address a, address delegatedUser, uint256 amount, uint256 index) {
env e;
uint256 balancebefore = balanceOf(e, a);
requireInvariant discountCantExceed100Percent(a);
mint(e, delegatedUser, a, amount, index);
burn(e, a, amount, index);
uint256 balanceAfter = balanceOf(e, a);
assert balancebefore == balanceAfter, "burn is not the inverse of mint";
}
rule integrityDelegationWithSig(address delegator, address delegatee, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) {
env e;
uint256 oldNonce = nonces(e, delegator);
delegationWithSig(e, delegator, delegatee, value, deadline, v, r, s);
assert nonces(e, delegator) == oldNonce + 1 && borrowAllowance(e, delegator, delegatee) == value;
}
/**
Burning user u amount of amount tokens, decreases his balanceOf the user by amount.
(balance is decreased by amount and not scaled amount because of the summarization to one ray)
*/
rule integrityOfBurn(address u, uint256 amount) {
env e;
uint256 index = indexAtTimestamp(e.block.timestamp);
uint256 balanceBeforeUser = balanceOf(e, u);
uint256 totalSupplyBefore = totalSupply();
burn(e, u, amount, index);
uint256 balanceAfterUser = balanceOf(e, u);
uint256 totalSupplyAfter = totalSupply();
assert bounded_error_eq(totalSupplyAfter, totalSupplyBefore - amount, 1, index), "total supply integrity"; // total supply reduced
assert bounded_error_eq(balanceAfterUser, balanceBeforeUser - amount, 1, index), "integrity break"; // user burns ATokens to receive underlying
}
rule integrityOfBurn_exact_supply_should_fail(address u, uint256 amount) {
env e;
uint256 index = indexAtTimestamp(e.block.timestamp);
uint256 balanceBeforeUser = balanceOf(e, u);
uint256 totalSupplyBefore = totalSupply();
burn(e, u, amount, index);
uint256 balanceAfterUser = balanceOf(e, u);
uint256 totalSupplyAfter = totalSupply();
assert totalSupplyAfter == totalSupplyBefore - amount, "total supply integrity"; // total supply reduced
}
rule integrityOfBurn_exact_balance_should_fail(address u, uint256 amount) {
env e;
uint256 index = indexAtTimestamp(e.block.timestamp);
uint256 balanceBeforeUser = balanceOf(e, u);
uint256 totalSupplyBefore = totalSupply();
burn(e, u, amount, index);
uint256 balanceAfterUser = balanceOf(e, u);
uint256 totalSupplyAfter = totalSupply();
assert totalSupplyAfter == totalSupplyBefore - amount, "total supply integrity"; // total supply reduced
}
/*
Burn is additive, can performed either all at once or gradually
burn(from,to,x,index); burn(from,to,y,index) ~ burn(from,to,x+y,index) at the same initial state
*/
rule additiveBurn(address user1, address user2, uint256 x, uint256 y) {
env e;
uint256 index = indexAtTimestamp(e.block.timestamp);
require (user1 != user2 && balanceOf(e, user1) == balanceOf(e, user2));
require user1 != currentContract && user2 != currentContract;
burn(e, user1, x, index);
burn(e, user1, y, index);
uint256 balanceScenario1 = balanceOf(e, user1);
burn(e, user2, x+y, index);
uint256 balanceScenario2 = balanceOf(e, user2);
assert bounded_error_eq(balanceScenario1, balanceScenario2, 3, index), "burn is not additive";
// assert balanceScenario1 == balanceScenario2, "burn is not additive";
}
// using too tight bound
rule additiveBurn_should_fail(address user1, address user2, uint256 x, uint256 y) {
env e;
uint256 index = indexAtTimestamp(e.block.timestamp);
require (user1 != user2 && balanceOf(e, user1) == balanceOf(e, user2));
require user1 != currentContract && user2 != currentContract;
burn(e, user1, x, index);
burn(e, user1, y, index);
uint256 balanceScenario1 = balanceOf(e, user1);
burn(e, user2, x+y, index);
uint256 balanceScenario2 = balanceOf(e, user2);
assert bounded_error_eq(balanceScenario1, balanceScenario2, 2, index), "burn is not additive";
//assert balanceScenario1 == balanceScenario2, "burn is not additive";
}
/*
Mint is additive, can performed either all at once or gradually
mint(from,to,x,index); mint(from,to,y,index) ~ mint(from,to,x+y,index) at the same initial state
*/
rule additiveMint(address user1, address user2, address user3, uint256 x, uint256 y) {
env e;
uint256 index = indexAtTimestamp(e.block.timestamp);
require (user1 != user2 && balanceOf(e, user1) == balanceOf(e, user2));
mint(e, user3, user1, x, index);
mint(e, user3, user1, y, index);
uint256 balanceScenario1 = balanceOf(e, user1);
mint(e, user3, user2, x+y, index);
uint256 balanceScenario2 = balanceOf(e, user2);
assert bounded_error_eq(balanceScenario1, balanceScenario2, 3, index), "burn is not additive";
// assert balanceScenario1 == balanceScenario2, "burn is not additive";
}
//using exact comparison
rule additiveMint_exact_should_fail(address user1, address user2, address user3, uint256 x, uint256 y) {
env e;
uint256 index = indexAtTimestamp(e.block.timestamp);
require (user1 != user2 && balanceOf(e, user1) == balanceOf(e, user2));
mint(e, user3, user1, x, index);
mint(e, user3, user1, y, index);
uint256 balanceScenario1 = balanceOf(e, user1);
mint(e, user3, user2, x+y, index);
uint256 balanceScenario2 = balanceOf(e, user2);
//assert bounded_error_eq(balanceScenario1, balanceScenario2, 3, index), "burn is not additive";
assert balanceScenario1 == balanceScenario2, "burn is not additive";
}
/**
Mint to user u amount of x tokens, increases his balanceOf the user by x.
(balance is increased by x and not scaled x because of the summarization to one ray)
*/
rule integrityMint(address a, uint256 x) {
env e;
address delegatedUser;
uint256 index = indexAtTimestamp(e.block.timestamp);
uint256 underlyingBalanceBefore = balanceOf(e, a);
uint256 atokenBalanceBefore = scaledBalanceOf(a);
uint256 totalATokenSupplyBefore = scaledTotalSupply(e);
mint(e, delegatedUser, a, x, index);
uint256 underlyingBalanceAfter = balanceOf(e, a);
uint256 atokenBalanceAfter = scaledBalanceOf(a);
uint256 totalATokenSupplyAfter = scaledTotalSupply(e);
assert atokenBalanceAfter - atokenBalanceBefore == totalATokenSupplyAfter - totalATokenSupplyBefore;
assert totalATokenSupplyAfter > totalATokenSupplyBefore;
assert bounded_error_eq(underlyingBalanceAfter, underlyingBalanceBefore+x, 1, index);
// assert balanceAfter == balancebefore+x;
}
//split rule to three - checking underlying alone
rule integrityMint_underlying(address a, uint256 x) {
env e;
address delegatedUser;
uint256 index = indexAtTimestamp(e.block.timestamp);
uint256 underlyingBalanceBefore = balanceOf(e, a);
uint256 atokenBalanceBefore = scaledBalanceOf(a);
uint256 totalATokenSupplyBefore = scaledTotalSupply(e);
mint(e, delegatedUser, a, x, index);
uint256 underlyingBalanceAfter = balanceOf(e, a);
uint256 atokenBalanceAfter = scaledBalanceOf(a);
uint256 totalATokenSupplyAfter = scaledTotalSupply(e);
//assert atokenBalanceAfter - atokenBalanceBefore == totalATokenSupplyAfter - totalATokenSupplyBefore;
//assert totalATokenSupplyAfter > totalATokenSupplyBefore;
assert bounded_error_eq(underlyingBalanceAfter, underlyingBalanceBefore+x, 1, index);
// assert balanceAfter == balancebefore+x;
}
//checking atoken alone
rule integrityMint_atoken(address a, uint256 x) {
env e;
address delegatedUser;
uint256 index = indexAtTimestamp(e.block.timestamp);
uint256 underlyingBalanceBefore = balanceOf(e, a);
uint256 atokenBalanceBefore = scaledBalanceOf(a);
uint256 totalATokenSupplyBefore = scaledTotalSupply(e);
mint(e, delegatedUser, a, x, index);
uint256 underlyingBalanceAfter = balanceOf(e, a);
uint256 atokenBalanceAfter = scaledBalanceOf(a);
uint256 totalATokenSupplyAfter = scaledTotalSupply(e);
assert atokenBalanceAfter - atokenBalanceBefore == totalATokenSupplyAfter - totalATokenSupplyBefore;
//assert totalATokenSupplyAfter > totalATokenSupplyBefore;
//assert bounded_error_eq(underlyingBalanceAfter, underlyingBalanceBefore+x, 1, index);
// assert balanceAfter == balancebefore+x;
}
rule integrityMint_exact_should_fail(address a, uint256 x) {
env e;
address delegatedUser;
uint256 index = indexAtTimestamp(e.block.timestamp);
uint256 underlyingBalanceBefore = balanceOf(e, a);
uint256 atokenBalanceBefore = scaledBalanceOf(a);
uint256 totalATokenSupplyBefore = scaledTotalSupply(e);
mint(e, delegatedUser, a, x, index);
uint256 underlyingBalanceAfter = balanceOf(e, a);
uint256 atokenBalanceAfter = scaledBalanceOf(a);
uint256 totalATokenSupplyAfter = scaledTotalSupply(e);
assert atokenBalanceAfter - atokenBalanceBefore == totalATokenSupplyAfter - totalATokenSupplyBefore;
assert totalATokenSupplyAfter > totalATokenSupplyBefore;
assert underlyingBalanceAfter == underlyingBalanceBefore+x;
}
// Burning zero amount of tokens should have no effect.
rule burnZeroDoesntChangeBalance(address u, uint256 index) {
env e;
uint256 balanceBefore = balanceOf(e, u);
burn@withrevert(e, u, 0, index);
uint256 balanceAfter = balanceOf(e, u);
assert balanceBefore == balanceAfter;
}
/*
Burning one user atokens should have no effect on other users that are not involved in the action.
*/
rule burnNoChangeToOther(address user, uint256 amount, uint256 index, address other) {
require other != user;
env e;
uint256 otherBalanceBefore = balanceOf(e, other);
burn(e, user, amount, index);
uint256 otherBalanceAfter = balanceOf(e, other);
assert otherBalanceBefore == otherBalanceAfter;
}
/*
Minting ATokens for a user should have no effect on other users that are not involved in the action.
*/
rule mintNoChangeToOther(address user, address onBehalfOf, uint256 amount, uint256 index, address other) {
require other != user && other != onBehalfOf;
env e;
uint256 userBalanceBefore = balanceOf(e, user);
uint256 otherBalanceBefore = balanceOf(e, other);
mint(e, user, onBehalfOf, amount, index);
uint256 userBalanceAfter = balanceOf(e, user);
uint256 otherBalanceAfter = balanceOf(e, other);
if (user != onBehalfOf) {
assert userBalanceBefore == userBalanceAfter ;
}
assert otherBalanceBefore == otherBalanceAfter ;
}
/*
Ensuring that the defined disallowed functions revert in any case.
*/
rule disallowedFunctionalities(method f)
filtered { f -> disAllowedFunctions(f) }
{
env e; calldataarg args;
f@withrevert(e, args);
assert lastReverted;
}
================================================
FILE: certora/gho/specs/erc20.spec
================================================
// erc20 methods
methods {
function _.name() external => DISPATCHER(true);
function _.symbol() external => DISPATCHER(true);
function _.decimals() external => DISPATCHER(true);
function _.totalSupply() external => DISPATCHER(true);
function _.balanceOf(address) external => DISPATCHER(true);
function _.allowance(address,address) external => DISPATCHER(true);
function _.approve(address,uint256) external => DISPATCHER(true);
function _.transfer(address,uint256) external => DISPATCHER(true);
function _.transferFrom(address,address,uint256) external => DISPATCHER(true);
}
================================================
FILE: certora/gho/specs/flashMinter.spec
================================================
using GhoToken as gho;
using GhoAToken as atoken;
using MockFlashBorrower as flashBorrower;
methods{
function _.isPoolAdmin(address user) external => retrievePoolAdminFromGhost(user) expect bool ALL;
function _.isFlashBorrower(address user) external => retrieveFlashBorrowerFromGhost(user) expect bool ALL;
function _.onFlashLoan(address, address, uint256, uint256, bytes) external => DISPATCHER(true);
function _.getACLManager() external => NONDET;
// FlashBorrower
function flashBorrower.action() external returns (MockFlashBorrower.Action) envfree;
function flashBorrower._transferTo() external returns (address) envfree;
function gho.allowance(address, address) external returns (uint256) envfree;
function _.burn(uint256) external=> DISPATCHER(true);
function _.mint(address, uint256) external=> DISPATCHER(true);
function _.transfer(address, uint256) external => DISPATCHER(true);
function _.balanceOf(address) external => DISPATCHER(true);
function _.decreaseBalanceFromInterest(address, uint256) external => NONDET;
function _.getBalanceFromInterest(address) external => NONDET;
function gho.totalSupply() external returns (uint256) envfree;
function gho.balanceOf(address) external returns (uint256) envfree;
function atoken.getGhoTreasury() external returns (address) envfree;
}
// keeps track of users with pool admin permissions in order to return a consistent value per user
ghost mapping(address => bool) poolAdmin_ghost;
// keeps track of users with flash borrower permissions in order to return a consistent value per user
ghost mapping(address => bool) flashBorrower_ghost;
// returns whether the user is a pool admin
function retrievePoolAdminFromGhost(address user) returns bool{
return poolAdmin_ghost[user];
}
// returns whether the user is a flash borrower
function retrieveFlashBorrowerFromGhost(address user) returns bool{
return flashBorrower_ghost[user];
}
// a set of assumptions needed for rules that call flashloan
function flashLoanReqs(env e){
require e.msg.sender != currentContract;
require gho.allowance(currentContract, e.msg.sender) == 0;
}
// an assumption that demands the sum of balances of 3 given users is no more than the total supply
function ghoBalanceOfTwoUsersLETotalSupply(address user1, address user2, address user3){
require gho.balanceOf(user1) + gho.balanceOf(user2) + gho.balanceOf(user3) <= to_mathint(gho.totalSupply());
}
/**
* @title The GHO balance of the flash minter should grow when calling any function, excluding distributeFees
*/
rule balanceOfFlashMinterGrows(method f, env e, calldataarg args)
filtered { f -> f.selector != sig:distributeFeesToTreasury().selector }{
// No overflow of gho is possible
ghoBalanceOfTwoUsersLETotalSupply(currentContract, e.msg.sender, atoken);
flashLoanReqs(e);
// excluding calls to distribute fees
mathint action = assert_uint256(flashBorrower.action());
require action != 1;
uint256 _facilitatorBalance = gho.balanceOf(currentContract);
f(e, args);
uint256 facilitatorBalance_ = gho.balanceOf(currentContract);
assert facilitatorBalance_ >= _facilitatorBalance;
}
/**
* @title Checks the integrity of updateGhoTreasury - after update the given address is set
*/
rule integrityOfTreasurySet(address token){
env e;
updateGhoTreasury(e, token);
address treasury_ = getGhoTreasury(e);
assert treasury_ == token;
}
/**
* @title Checks the integrity of updateFee - after update the given value is set
*/
rule integrityOfFeeSet(uint256 new_fee){
env e;
updateFee(e, new_fee);
uint256 fee_ = getFee(e);
assert fee_ == new_fee;
}
/**
* @title Checks that the available liquidity, retrieved by maxFlashLoan, stays the same after any action
*/
rule availableLiquidityDoesntChange(method f, address token){
env e; calldataarg args;
uint256 _liquidity = maxFlashLoan(e, token);
f(e, args);
uint256 liquidity_ = maxFlashLoan(e, token);
assert liquidity_ == _liquidity;
}
/**
* @title Checks the integrity of distributeFees:
* 1. As long as the treasury contract itself isn't acting as a flashloan minter, the flashloan facilitator's GHO balance should be empty after distribution
* 2. The change in balances of the receiver (treasury) and the sender (flash minter) is the same. i.e. no money is being generated out of thin air
*/
rule integrityOfDistributeFeesToTreasury(){
env e;
address treasury = getGhoTreasury(e);
uint256 _facilitatorBalance = gho.balanceOf(currentContract);
uint256 _treasuryBalance = gho.balanceOf(treasury);
// No overflow of gho is possible
ghoBalanceOfTwoUsersLETotalSupply(currentContract, treasury, atoken);
distributeFeesToTreasury(e);
uint256 facilitatorBalance_ = gho.balanceOf(currentContract);
uint256 treasuryBalance_ = gho.balanceOf(treasury);
assert treasury != currentContract => facilitatorBalance_ == 0;
assert treasuryBalance_ - _treasuryBalance == _facilitatorBalance - facilitatorBalance_;
}
/**
* @title Checks that the fee amount reported by flashFee is the the same as the actual fee that is taken by flashloaning
*/
rule feeSimulationEqualsActualFee(address receiver, address token, uint256 amount, bytes data){
env e;
mathint feeSimulationResult = flashFee(e, token, amount);
uint256 _facilitatorBalance = gho.balanceOf(currentContract);
flashLoanReqs(e);
require atoken.getGhoTreasury() != currentContract;
// No overflow of gho is possible
ghoBalanceOfTwoUsersLETotalSupply(currentContract, e.msg.sender, atoken);
// Excluding call to distributeFeesToTreasury & calling another flashloan (which will generate another fee in recursion)
mathint borrower_action = assert_uint256(flashBorrower.action());
require borrower_action != 1 && borrower_action != 0;
// Because we calculate the actual fee by balance difference of the minter, we assume no extra money is being sent to the minter.
require flashBorrower._transferTo() != currentContract;
flashLoan(e, receiver, token, amount, data);
uint256 facilitatorBalance_ = gho.balanceOf(currentContract);
mathint actualFee = facilitatorBalance_ - _facilitatorBalance;
assert feeSimulationResult == actualFee;
}
rule sanity {
env e;
calldataarg arg;
method f;
f(e, arg);
satisfy true;
}
================================================
FILE: certora/gho/specs/ghoAToken.spec
================================================
import "erc20.spec";
using GhoTokenHarness as _ghoTokenHarness;
methods{
function totalSupply() external returns (uint256) envfree;
function RESERVE_TREASURY_ADDRESS() external returns (address) envfree;
function UNDERLYING_ASSET_ADDRESS() external returns (address) envfree;
function transferUnderlyingTo(address,uint256) external;
function handleRepayment(address,address,uint256) external;
function distributeFeesToTreasury() external envfree ;
function rescueTokens(address,address,uint256) external;
function setVariableDebtToken(address) external;
function getVariableDebtToken() external returns (address) envfree;
function updateGhoTreasury(address) external ;
function getGhoTreasury() external returns (address) envfree;
function _ghoTokenHarness.getFacilitatorBucketCapacity(address) external returns (uint256) envfree;
function _ghoTokenHarness.getFacilitatorBucketLevel(address) external returns (uint256) envfree;
function _ghoTokenHarness.balanceOf(address) external returns (uint256) envfree;
function scaledBalanceOf(address) external returns (uint256) envfree;
/*******************
* Pool.sol *
********************/
function _.getReserveNormalizedIncome(address) external => CONSTANT;
/***********************************
* PoolAddressesProvider.sol *
************************************/
function _.getACLManager() external => CONSTANT;
/************************
* ACLManager.sol *
*************************/
function _.isPoolAdmin(address) external => CONSTANT;
}
/**
* @title Proves that ghoAToken::mint always reverts
**/
rule noMint() {
env e;
calldataarg args;
mint(e, args);
assert(false);
}
/**
* @title Proves that ghoAToken::burn always reverts
**/
rule noBurn() {
env e;
calldataarg args;
burn(e, args);
assert(false);
}
/**
* @title Proves that ghoAToken::transfer always reverts
**/
rule noTransfer() {
env e;
calldataarg args;
transfer(e, args);
assert(false);
}
/**
* @title Proves that calling ghoAToken::transferUnderlyingTo will revert if the amount exceeds the excess capacity
* @notice A user can’t borrow more than the facilitator’s remaining capacity.
**/
rule transferUnderlyingToCantExceedCapacity() {
address target;
uint256 amount;
env e;
mathint facilitatorLevel = _ghoTokenHarness.getFacilitatorBucketLevel(currentContract);
mathint facilitatorCapacity = _ghoTokenHarness.getFacilitatorBucketCapacity(currentContract);
transferUnderlyingTo@withrevert(e, target, amount);
assert(to_mathint(amount) > (facilitatorCapacity - facilitatorLevel) => lastReverted);
}
/**
* @title Proves that the total supply of GhoAToken is always zero
**/
rule totalSupplyAlwaysZero() {
assert(totalSupply() == 0);
}
/**
* @title Proves that any user's balance of GhoAToken is always zero
**/
invariant userBalanceAlwaysZero(address user)
scaledBalanceOf(user) == 0;
// /**
// * @title first handleRepayment(amount) after transferUnderlyingTo(amount) succeeds.
// * @dev assumption of sufficient balanceOf(msg.sender) is justified because BorrowLogic.executeRepay()
// * @dev executes: IERC20(params.asset).safeTransferFrom(msg.sender, reserveCache.aTokenAddress, paybackAmount);
// * @dev before invocation of handleRepayment()
// * OBSOLETE - GhoToken has other rules to validate the behavior of the facilitator level maintenance
// */
// rule handleRepayment_after_transferUnderlyingTo()
// {
// env e;
// calldataarg arg;
// uint256 amount;
// address target;
// address user;
// address onBehalfOf;
// transferUnderlyingTo(e, target, amount);
// require _ghoTokenHarness.balanceOf(e.msg.sender) >= amount; //underlying asset
// require e.msg.sender == currentContract;
// handleRepayment@withrevert(e, user, onBehalfOf, amount);
// assert !lastReverted, "handleRepayment failed";
// }
/**
* @title BucketLevel decreases after transferUnderlyingTo() followed by handleRepayment()
* @dev repayment funds are, partially or fully, transferred to the treasury
*/
rule level_does_not_decrease_after_transferUnderlyingTo_followed_by_handleRepayment()
{
env e;
calldataarg arg;
uint256 amount;
address target;
address user;
address onBehalfOf;
uint256 levelBefore = _ghoTokenHarness.getFacilitatorBucketLevel(currentContract);
transferUnderlyingTo(e, target, amount);
handleRepayment(e, user, onBehalfOf, amount);
uint256 levelAfter = _ghoTokenHarness.getFacilitatorBucketLevel(currentContract);
assert levelBefore <= levelAfter;
}
================================================
FILE: certora/gho/specs/ghoDiscountRateStrategy.spec
================================================
methods {
function calculateDiscountRate(uint256, uint256) external returns (uint256) envfree;
function MIN_DISCOUNT_TOKEN_BALANCE() external returns (uint256) envfree;
function MIN_DEBT_TOKEN_BALANCE() external returns (uint256) envfree;
function DISCOUNT_RATE() external returns (uint256) envfree;
function GHO_DISCOUNTED_PER_DISCOUNT_TOKEN() external returns (uint256) envfree;
function wadMul(uint256, uint256) external returns (uint256) envfree;
}
function wad() returns uint256 {
return 10^18;
}
function wadMulCVL(uint256 a, uint256 b) returns mathint {
return ((a * b + (wad() / 2)) / wad());
}
/**
* @title sanity rule, checks that all contract functions are executables, should fail.
**/
// rule sanity(method f) {
// env e;
// calldataarg args;
// f(e, args);
// assert(false);
// }
/**
* @title prove the equivalence between wadMulCVL and the solidity implementation of wadMul
**/
rule equivalenceOfWadMulCVLAndWadMulSol() {
uint256 x;
uint256 y;
mathint wadMulCvl = wadMulCVL(x, y);
uint256 wadMulSol = wadMul(x, y);
assert(wadMulCvl == to_mathint(wadMulSol));
}
/**
* @title proves that if the account's entitled balance for discount is above its current debt balance than the discount rate is the maximal rate
**/
rule maxDiscountForHighDiscountTokenBalance() {
uint256 debtBalance;
uint256 discountTokenBalance;
mathint discountedBalance = wadMulCVL(GHO_DISCOUNTED_PER_DISCOUNT_TOKEN(), discountTokenBalance);
uint256 rate = calculateDiscountRate(debtBalance, discountTokenBalance);
// forcing the debt/discount token balance to be above the minimal value allowed in order to get a non-zero rate
require(debtBalance >= MIN_DEBT_TOKEN_BALANCE() && discountTokenBalance >= MIN_DISCOUNT_TOKEN_BALANCE());
assert(discountedBalance >= to_mathint(debtBalance) => rate == DISCOUNT_RATE());
}
/**
* @title proves that the discount balance below the threshold leads to zero discount rate
**/
rule zeroDiscountForSmallDiscountTokenBalance() {
uint256 debtBalance;
uint256 discountTokenBalance;
uint256 rate = calculateDiscountRate(debtBalance, discountTokenBalance);
mathint discountedBalance = wadMulCVL(GHO_DISCOUNTED_PER_DISCOUNT_TOKEN(), discountTokenBalance);
// there are three conditions that can result in a zero rate:
// 1,2 - if the debt balance or the discount token balance are below some threshold.
// 3 - if debtBalance is much larger than discountBalance (since the return value is the max rate multiplied
// by the ratio between debtBalance and discountBalance)
assert(
(debtBalance < MIN_DEBT_TOKEN_BALANCE() ||
discountTokenBalance < MIN_DISCOUNT_TOKEN_BALANCE() ||
discountedBalance*DISCOUNT_RATE() < to_mathint(debtBalance))
<=> rate == 0);
}
/**
* @title if the discounted balance is above the threshold and below the current debt, the discount rate will be according to the ratio
* between the debt balance and the discounted balance
**/
rule partialDiscountForIntermediateTokenBalance() {
uint256 debtBalance;
uint256 discountTokenBalance;
mathint discountedBalance = wadMulCVL(GHO_DISCOUNTED_PER_DISCOUNT_TOKEN(), discountTokenBalance);
uint256 rate = calculateDiscountRate(debtBalance, discountTokenBalance);
require(debtBalance >= MIN_DEBT_TOKEN_BALANCE() && discountTokenBalance >= MIN_DISCOUNT_TOKEN_BALANCE());
assert(discountedBalance < to_mathint(debtBalance) => (to_mathint(rate) == (discountedBalance * DISCOUNT_RATE()) / debtBalance));
}
/**
* @title proves that the discount rate is caped by the maximal discount rate value
**/
rule limitOnDiscountRate() {
uint256 debtBalance;
uint256 discountTokenBalance;
uint256 discountRate = calculateDiscountRate(debtBalance, discountTokenBalance);
assert(discountRate <= DISCOUNT_RATE());
}
rule sanity {
env e;
calldataarg arg;
method f;
f(e, arg);
satisfy true;
}
================================================
FILE: certora/gho/specs/ghoToken.spec
================================================
import "set.spec";
methods{
function mint(address,uint256) external;
function burn(uint256) external;
function removeFacilitator(address) external;
function setFacilitatorBucketCapacity(address,uint128) external;
function totalSupply() external returns uint256 envfree;
function balanceOf(address) external returns (uint256) envfree;
function getFacilitatorBucketLevel(address) external returns uint256 envfree;
function getFacilitatorBucketCapacity(address) external returns uint256 envfree;
function is_in_facilitator_mapping(address) external returns bool envfree;
function is_in_facilitator_set_map(address) external returns bool envfree;
function is_in_facilitator_set_array(address) external returns bool envfree;
}
ghost sumAllBalance() returns mathint {
init_state axiom sumAllBalance() == 0;
}
hook Sstore balanceOf[KEY address a] uint256 balance (uint256 old_balance) STORAGE {
havoc sumAllBalance assuming sumAllBalance@new() == sumAllBalance@old() + balance - old_balance;
}
hook Sload uint256 balance balanceOf[KEY address a] STORAGE {
require to_mathint(balance) <= sumAllBalance();
}
ghost sumAllLevel() returns mathint {
init_state axiom sumAllLevel() == 0;
}
/**
* @title Sum of facilitators' bucket levels
* @dev Sample stores to _facilitators[*].bucketLevel
* @dev first field of struct Facilitator is uint128 so offset 16 is used
**/
hook Sstore _facilitators[KEY address a].(offset 16) uint128 level (uint128 old_level) STORAGE {
havoc sumAllLevel assuming sumAllLevel@new() == sumAllLevel@old() + level - old_level;
}
//
// Invariants
//
// INV #1
/**
* @title Length of AddressSet is less than 2^160
* @dev the assumption is safe because there are at most 2^160 unique addresses
* @dev the proof of the assumption is vacuous because length > loop_iter
*/
invariant length_leq_max_uint160()
getFacilitatorsListLen() < TWO_TO_160();
// INV #2
/**
* @title User's balance not greater than totalSupply()
*/
invariant inv_balanceOf_leq_totalSupply(address user)
balanceOf(user) <= totalSupply()
{
preserved {
requireInvariant sumAllBalance_eq_totalSupply();
}
}
// INV #3
/**
* @title Sum of bucket levels is equals to GhoToken::totalSupply()
**/
invariant total_supply_eq_sumAllLevel()
sumAllLevel() == to_mathint(totalSupply())
{
preserved burn(uint256 amount) with (env e){
requireInvariant inv_balanceOf_leq_totalSupply(e.msg.sender);
}
}
// INV #4
/**
* @title Sum of balances is GhoToke::totalSupply()
* @dev EITHER requireInvariant sumAllLevel_eq_sumAllBalance() OR requireInvariant total_supply_eq_sumAllLevel() suffices.
**/
//todo: replace preserve
invariant sumAllBalance_eq_totalSupply()
sumAllBalance() == to_mathint(totalSupply())
{
preserved {
requireInvariant sumAllLevel_eq_sumAllBalance();
}
}
// INV #5
/**
* @title The sum of bucket level is equal to the sum of GhoToken balances
* @dev This invariant can be deduced from sumAllBalance_eq_totalSupply and total_supply_eq_sumAllLevel
* @dev requireInvariant of EITHER sumAllBalance_eq_totalSupply() OR total_supply_eq_sumAllLevel() suffuces for the proof
**/
invariant sumAllLevel_eq_sumAllBalance()
sumAllLevel() == sumAllBalance()
{
preserved {
requireInvariant sumAllBalance_eq_totalSupply();
}
}
// INV #6
/**
* @title A facilitator with a positive bucket capacity exists in the _facilitators mapping
*/
invariant inv_valid_capacity(address facilitator)
((getFacilitatorBucketCapacity(facilitator)>0) => is_in_facilitator_mapping(facilitator) );
// INV #7
/**
* @title A facilitator with a positive bucket level exists in the _facilitators mapping
*/
invariant inv_valid_level(address facilitator)
((getFacilitatorBucketLevel(facilitator)>0) => is_in_facilitator_mapping(facilitator) )
{
preserved{
requireInvariant inv_valid_capacity(facilitator);
}
}
// INV #8
/**
* @title AddressSet internal coherency
* @dev A facilitator address exists in AddressSet list (GhoToken._facilitatorsList._values)
* @dev if and only if it exists in AddressSet mapping (GhoToken._facilitatorsList._indexes)
*/
invariant address_in_set_values_iff_in_set_indexes(address facilitator)
is_in_facilitator_set_array(facilitator) <=> is_in_facilitator_set_map(facilitator)
{preserved{
requireInvariant addressSetInvariant();
requireInvariant length_leq_max_uint160();
}
}
// INV #9
/**
* @title GhoToken mapping-AddressSet coherency (1)
* @dev A facilitator address that exists in GhoToken Facilitator mapping (GhoToken._facilitators)
* @dev if and only if it exists in GhoToken AddressSet (GhoToken._facilitatorsList._indexes)
*/
invariant addr_in_set_iff_in_map(address facilitator)
is_in_facilitator_mapping(facilitator) <=> is_in_facilitator_set_map(facilitator)
{preserved{
requireInvariant addressSetInvariant();
}
}
// INV #10
/**
* @title GhoToken mapping-AddressSet coherency (2)
* @dev A facilitator address exists in GhoToken Facilitator mapping (GhoToken._facilitators)
* @dev iff it exists in GhoToken AddressSet list (GhoToken._facilitatorsList._values)
*/
invariant addr_in_set_list_iff_in_map(address facilitator)
is_in_facilitator_mapping(facilitator) <=> is_in_facilitator_set_array(facilitator)
{preserved{
requireInvariant addressSetInvariant();
requireInvariant length_leq_max_uint160();
}
}
//
// Rules
//
/**
* @title Bucket level <= bucket capacity unless setFacilitatorBucketCapacity() lowered it
*/
rule level_leq_capacity(address facilitator, method f) filtered {f -> !f.isView}{
env e;
calldataarg arg;
requireInvariant inv_valid_capacity(facilitator);
require getFacilitatorBucketLevel(facilitator) <= getFacilitatorBucketCapacity(facilitator);
f(e, arg);
assert ((f.selector != sig:setFacilitatorBucketCapacity(address,uint128).selector)
=> (getFacilitatorBucketLevel(facilitator) <= getFacilitatorBucketCapacity(facilitator)));
}
/**
* @notice If Bucket level < bucket capacity then the first invocation of mint() succeeds after burn
* @notice unless setFacilitatorBucketCapacity() lowered bucket capacity or removeFacilitator() was called
*/
rule mint_after_burn(method f) filtered {f -> !f.isView}
{
env e;
calldataarg arg;
uint256 amount_burn;
uint256 amount_mint;
address account;
require getFacilitatorBucketLevel(e.msg.sender) <= getFacilitatorBucketCapacity(e.msg.sender);
require amount_mint > 0;
requireInvariant addressSetInvariant();
requireInvariant inv_balanceOf_leq_totalSupply(e.msg.sender);
requireInvariant inv_valid_capacity(e.msg.sender);
burn(e, amount_burn);
f(e, arg);
mint@withrevert(e, account, amount_mint);
assert (((amount_mint <= amount_burn)
&& f.selector != sig:mint(address,uint256).selector
&& f.selector != sig:setFacilitatorBucketCapacity(address,uint128).selector
&& f.selector != sig:removeFacilitator(address).selector
) => !lastReverted), "mint failed";
}
/**
* @title Burn after mint succeeds
* @dev BorrowLogic::executeRepa() executes the following code before invocation of handleRepayment()
* @dev safeTransferFrom(msg.sender, reserveCache.aTokenAddress, paybackAmount);
*/
rule burn_after_mint(method f) filtered {f -> !f.isView}
{
env e;
uint256 amount;
address account;
requireInvariant inv_balanceOf_leq_totalSupply(e.msg.sender);
require e.msg.value == 0;
require amount > 0;
mint(e, account, amount);
transferFrom(e, account, e.msg.sender, amount);
burn@withrevert(e, amount);
assert !lastReverted, "burn failed";
}
/**
* @title BucketLevel remains unchanged after mint() followed by burn()
*/
rule level_unchanged_after_mint_followed_by_burn()
{
env e;
calldataarg arg;
uint256 amount;
address account;
uint256 levelBefore = getFacilitatorBucketLevel(e.msg.sender);
mint(e, account, amount);
burn(e, amount);
uint256 leveAfter = getFacilitatorBucketLevel(e.msg.sender);
assert levelBefore == leveAfter;
}
rule level_after_mint()
{
env e;
calldataarg arg;
uint256 amount;
address account;
uint256 levelBefore = getFacilitatorBucketLevel(e.msg.sender);
mint(e, account, amount);
uint256 leveAfter = getFacilitatorBucketLevel(e.msg.sender);
assert levelBefore + amount == to_mathint(leveAfter);
}
rule level_after_burn()
{
env e;
calldataarg arg;
uint256 amount;
uint256 levelBefore = getFacilitatorBucketLevel(e.msg.sender);
burn(e, amount);
uint256 leveAfter = getFacilitatorBucketLevel(e.msg.sender);
assert to_mathint(levelBefore) == leveAfter + amount;
}
/**
* @title Facilitator is valid after successful call to setFacilitatorBucketCapacity()
*/
rule facilitator_in_list_after_setFacilitatorBucketCapacity(){
env e;
address facilitator;
uint128 newCapacity;
requireInvariant addr_in_set_iff_in_map(facilitator);
requireInvariant addr_in_set_list_iff_in_map(facilitator);
setFacilitatorBucketCapacity(e, facilitator, newCapacity);
assert is_in_facilitator_set_map(facilitator);
assert is_in_facilitator_set_array(facilitator);
}
/**
* @title getFacilitatorBucketCapacity() called after setFacilitatorBucketCapacity() return the assign bucket capacity
*/
rule getFacilitatorBucketCapacity_after_setFacilitatorBucketCapacity(){
env e;
address facilitator;
uint128 newCapacity;
setFacilitatorBucketCapacity(e, facilitator, newCapacity);
assert getFacilitatorBucketCapacity(facilitator) == require_uint256(newCapacity);
}
/**
* @title Facilitator is valid after successful call to addFacilitator()
*/
rule facilitator_in_list_after_addFacilitator(){
env e;
address facilitator;
string label;
uint128 capacity;
requireInvariant addr_in_set_iff_in_map(facilitator);
addFacilitator(e,facilitator, label, capacity);
assert is_in_facilitator_set_map(facilitator);
assert is_in_facilitator_set_array(facilitator);
}
/**
* @title Facilitator is valid after successful call to mint() or burn()
*/
rule facilitator_in_list_after_mint_and_burn(method f){
env e;
calldataarg args;
requireInvariant inv_valid_capacity(e.msg.sender);
requireInvariant inv_valid_level(e.msg.sender);
requireInvariant addr_in_set_iff_in_map(e.msg.sender);
requireInvariant addr_in_set_list_iff_in_map(e.msg.sender);
f(e,args);
assert (((f.selector == sig:mint(address,uint256).selector) || (f.selector == sig:burn(uint256).selector)) => is_in_facilitator_mapping(e.msg.sender));
assert (((f.selector == sig:mint(address,uint256).selector) || (f.selector == sig:burn(uint256).selector)) => is_in_facilitator_set_map(e.msg.sender));
assert (((f.selector == sig:mint(address,uint256).selector) || (f.selector == sig:burn(uint256).selector)) => is_in_facilitator_set_array(e.msg.sender));
}
/**
* @title Facilitator address is removed from list (GhoToken._facilitatorsList._values) after calling removeFacilitator()
**/
rule address_not_in_list_after_removeFacilitator(address facilitator){
env e;
requireInvariant addressSetInvariant();
requireInvariant length_leq_max_uint160();
requireInvariant addr_in_set_iff_in_map(facilitator);
removeFacilitator(e, facilitator);
assert !is_in_facilitator_set_array(facilitator);
}
/**
* @title Proves that mint(a + b) == mint(a) + mint(b)
**/
// rule mintIsAdditive() {
// address user1;
// address user2;
// require (user1 != user2);
// uint256 initBalance1 = balanceOf(user1);
// uint256 initBalance2 = balanceOf(user2);
// require (sumAllBalance() >= initBalance1 + initBalance2);
// requireInvariant sumAllBalance_eq_totalSupply();
// uint256 amount1;
// uint256 amount2;
// uint256 sum = amount1 + amount2;
// env e;
// mint(e, user1, amount1);
// mint(e, user1, amount2);
// mint(e, user2, sum);
// uint256 finBalance1 = balanceOf(user1);
// uint256 finBalance2 = balanceOf(user2);
// mathint diff1 = finBalance1 - initBalance1;
// mathint diff2 = finBalance2 - initBalance2;
// assert diff1 == diff2;
// }
rule balance_after_mint() {
env e;
address user;
uint256 initBalance = balanceOf(user);
uint256 initSupply = totalSupply();
uint256 amount;
requireInvariant sumAllBalance_eq_totalSupply();
mint(e, user, amount);
uint256 finBalance = balanceOf(user);
uint256 finSupply = totalSupply();
assert initBalance + amount == to_mathint(finBalance);
assert initSupply + amount == to_mathint(finSupply);
}
rule balance_after_burn() {
env e;
requireInvariant inv_balanceOf_leq_totalSupply(e.msg.sender);
uint256 initBalance = balanceOf(e.msg.sender);
uint256 initSupply = totalSupply();
uint256 amount;
burn(e, amount);
uint256 finBalance = balanceOf(e.msg.sender);
uint256 finSupply = totalSupply();
assert to_mathint(initBalance) == finBalance + amount;
assert to_mathint(initSupply) == finSupply + amount ;
}
/**
* @title Proves that burn(a + b) == burn(a) + burn(b)
**/
// rule burnIsAdditive() {
// env e;
// uint256 senderBalance = balanceOf(e.msg.sender);
// require(senderBalance <= sumAllBalance());
// requireInvariant sumAllBalance_eq_totalSupply();
// uint256 amount1;
// uint256 amount2;
// uint256 sum = amount1 + amount2;
// uint256 initSupply = totalSupply();
// burn(e, amount1);
// burn(e, amount2);
// uint256 midSupply = totalSupply();
// burn(e, sum);
// uint256 finSupply = totalSupply();
// mathint diff1 = finSupply - midSupply;
// mathint diff2 = midSupply - initSupply;
// assert diff1 == diff2;
// }
/**
* @title Proves that you can't mint more than the facilitator's remaining capacity
**/
rule mintLimitedByFacilitatorRemainingCapacity() {
env e;
require(getFacilitatorBucketCapacity(e.msg.sender) > getFacilitatorBucketLevel(e.msg.sender));
uint256 amount;
require(to_mathint(amount) > (getFacilitatorBucketCapacity(e.msg.sender) - getFacilitatorBucketLevel(e.msg.sender)));
address user;
mint@withrevert(e, user, amount);
assert lastReverted;
}
/**
* @title Proves that you can't burn more than the facilitator's current level
**/
rule burnLimitedByFacilitatorLevel() {
env e;
require(getFacilitatorBucketCapacity(e.msg.sender) > getFacilitatorBucketLevel(e.msg.sender));
uint256 amount;
require(amount > getFacilitatorBucketLevel(e.msg.sender));
burn@withrevert(e, amount);
assert lastReverted;
}
//
// Additional rules
//
//keep these rules for development team - resolve timeouts, fix bugs
//pass with workaround for https://certora.atlassian.net/browse/CERT-1060
invariant ARRAY_IS_INVERSE_OF_MAP_Invariant()
ARRAY_IS_INVERSE_OF_MAP()
{
preserved{
require ADDRESS_SET_INVARIANT();
requireInvariant length_leq_max_uint160();
}
}
//pass with workaround for https://certora.atlassian.net/browse/CERT-1060
invariant addressSetInvariant()
ADDRESS_SET_INVARIANT()
{
preserved{
requireInvariant length_leq_max_uint160();
}
}
//Debugging https://certora.atlassian.net/browse/CERT-1060
//timeout with staging
//fail with yoav/grounding
//pass with axiom mirrorArrayLen < TWO_TO_160() - 1
rule address_not_in_list_after_removeFacilitator_CASE_SPLIT_zero_address(address facilitator){
env e;
requireInvariant addressSetInvariant();
require facilitator == 0;
requireInvariant addr_in_set_iff_in_map(facilitator);
removeFacilitator(e, facilitator);
assert !is_in_facilitator_set_array(facilitator);
}
================================================
FILE: certora/gho/specs/ghoVariableDebtToken-rayMulDiv-summarization.spec
================================================
//import "erc20.spec"
import "VariableDebtToken.spec";
import "summarizations.spec";
using GhoDiscountRateStrategy as discStrategy;
methods{
/********************;
* WadRayMath.sol *;
*********************/
function _.rayMul(uint256 a,uint256 b) internal => rayMul_gst(a,b) expect uint256 ALL;
function _.rayDiv(uint256 a,uint256 b) internal => rayDiv_gst(a,b) expect uint256 ALL;
function getDiscountPercent(address user) external returns (uint256) envfree;
function get_ghoAToken() external returns (address) envfree;
/***********************************;
* PoolAddressesProvider.sol *;
************************************/
function _.getACLManager() external => CONSTANT;
/************************;
* ACLManager.sol *;
*************************/
function _.isPoolAdmin(address) external => CONSTANT;
/******************************************************************;
* DummyERC20WithTimedBalanceOf.sol (linked to _discountToken) *;
*******************************************************************/
function _._balanceOfWithBlockTimestamp(address user, uint256 ts) internal => balanceOfDiscountTokenAtTimestamp(user, ts) expect uint256;
/************************************;
* DummyPool.sol (linked to POOL) *;
*************************************/
function _._getReserveNormalizedVariableDebtWithBlockTimestamp(address asset, uint256 timestamp) internal => indexAtTimestamp(timestamp) expect uint256;
/************************************;
* GhoVariableDebtTokenHarness.sol *;
*************************************/
function discStrategy.calculateDiscountRate(uint256, uint256) external returns (uint256) envfree;
/************************************;
* GhoVariableDebtTokenHarness.sol *;
*************************************/
function getUserCurrentIndex(address) external returns (uint256) envfree;
function getUserDiscountRate(address) external returns (uint256) envfree;
function getUserAccumulatedDebtInterest(address) external returns (uint256) envfree;
function getBalanceOfDiscountToken(address) external returns (uint256);
/********************************;
* GhoVariableDebtToken.sol *;
*********************************/
function totalSupply() external returns(uint256) envfree;
function balanceOf(address) external returns (uint256);
function mint(address, address, uint256, uint256) external returns (bool, uint256);
function burn(address ,uint256 ,uint256) external returns (uint256);
function scaledBalanceOf(address) external returns (uint256) envfree;
function getBalanceFromInterest(address) external returns (uint256) envfree;
function rebalanceUserDiscountPercent(address) external;
function updateDiscountDistribution(address ,address ,uint256 ,uint256 ,uint256) external;
}
ghost rayMul_gst(mathint , mathint) returns uint256 {
//axiom 1==1;
axiom forall mathint x. forall mathint y. //rayMul_gst(x,y)+0 == x;
(
((x==0||y==0) => rayMul_gst(x,y)==0)
&&
x <= to_mathint(rayMul_gst(x,y)) && to_mathint(rayMul_gst(x,y)) <= 2*x
) ;
}
ghost rayDiv_gst(mathint , mathint) returns uint256 {
// axiom 1==1;
axiom forall mathint x. forall mathint y. //rayDiv_gst(x,y)+0 == x;
(
x/2 <= to_mathint(rayDiv_gst(x,y)) && to_mathint(rayDiv_gst(x,y)) <= x
);
}
definition MAX_DISCOUNT() returns uint256 = 10000; // equals to 100% discount, in points
ghost mapping(address => mapping (uint256 => uint256)) discount_ghost;
ghost mapping(uint256 => uint256) index_ghost;
/**
* Query index_ghost for the index value at the input timestamp
**/
function indexAtTimestamp(uint256 timestamp) returns uint256 {
require index_ghost[timestamp] >= ray();
return index_ghost[timestamp];
}
/**
* Query discount_ghost for the [user]'s balance of discount token at [timestamp]
**/
function balanceOfDiscountTokenAtTimestamp(address user, uint256 timestamp) returns uint256 {
return discount_ghost[user][timestamp];
}
function get_discount_scaled(env e, address user, uint256 current_index) returns uint256 {
uint256 user_scaledBal_prev = scaledBalanceOf(user);
// assert (user_scaledBal_after <= user_scaledBal_prev);
// uint256 current_index = indexAtTimestamp(e.block.timestamp);
uint256 user_index = getUserCurrentIndex(user);
require user_index <= current_index;
//uint256 bal_increase = (current_index-sender_index) * previousScaledBalance_of_sender;
mathint bal_increase = rayMul_gst(user_scaledBal_prev, current_index) -
rayMul_gst(user_scaledBal_prev, user_index);
//uint256 discountScaled = bal_increase * sender_precentage / index;
uint256 discountPercent = getDiscountPercent(e, user);
uint256 discount = require_uint256(bal_increase * discountPercent / MAX_DISCOUNT());
uint256 discountScaled = rayDiv_gst(discount, current_index);
return discountScaled;
}
/*================================================================================
Calling to mint(...amount) can't increase the scaled balance by more than scaled-amount.
(This catches mutant 11)
=================================================================================*/
rule mint_cant_increase_bal_by_more_than_amountScaled() {
env e;
address user; address onBehalfOf; uint256 amount; uint256 index;
require getUserCurrentIndex(onBehalfOf) <= index;
uint256 amountScaled = rayDiv_gst(amount,index);
uint256 prev_bal = scaledBalanceOf(e, onBehalfOf);
mint(e,user,onBehalfOf,amount,index);
mathint after_bal = scaledBalanceOf(e, onBehalfOf);
assert (after_bal <= prev_bal + amountScaled);
}
/*================================================================================
When calling updateDiscountDistribution, if discountScaled>0 then the
balance of the sender must decrease.
(This catches mutant 5)
=================================================================================*/
rule discount_takes_place_in_updateDiscountDistribution__sender() {
env e;
address sender; address recipient;
uint256 senderDiscountTokenBalance; uint256 recipientDiscountTokenBalance; uint256 amount;
uint256 sender_scaledBal_prev = scaledBalanceOf(sender);
uint256 discountScaled = get_discount_scaled(e,sender,indexAtTimestamp(e.block.timestamp));
updateDiscountDistribution(e, sender, recipient,
senderDiscountTokenBalance, recipientDiscountTokenBalance,
amount);
uint256 sender_scaledBal_after = scaledBalanceOf(sender);
if (sender != recipient){
assert (discountScaled > 0 => sender_scaledBal_after<sender_scaledBal_prev);
}
else{
assert (sender_scaledBal_after == sender_scaledBal_prev);
}
}
/*================================================================================
When calling updateDiscountDistribution, if discountScaled>0 then the
balance of the recipient must decrease.
(This catches mutant 13)
=================================================================================*/
rule discount_takes_place_in_updateDiscountDistribution__recipient() {
env e;
address sender; address recipient;
uint256 senderDiscountTokenBalance; uint256 recipientDiscountTokenBalance; uint256 amount;
uint256 recipient_scaledBal_prev = scaledBalanceOf(recipient);
uint256 discountScaled = get_discount_scaled(e,recipient,indexAtTimestamp(e.block.timestamp));
updateDiscountDistribution(e, sender, recipient,
senderDiscountTokenBalance, recipientDiscountTokenBalance,
amount);
uint256 recipient_scaledBal_after = scaledBalanceOf(recipient);
if (sender != recipient){
assert (discountScaled > 0 => recipient_scaledBal_after<recipient_scaledBal_prev);
}
else{
assert (recipient_scaledBal_after == recipient_scaledBal_prev);
}
}
/*================================================================================
When calling updateDiscountDistribution, the argument 'amount' has influence
on the discountPercent of the sender.
(This catches mutant 10)
=================================================================================*/
rule in_updateDiscountDistribution_amount_affects_sender_discount() {
env e;
address sender; address recipient;
uint256 senderDiscountTokenBalance; uint256 recipientDiscountTokenBalance; uint256 amount_1;
storage initState = lastStorage;
updateDiscountDistribution(e, sender, recipient,
senderDiscountTokenBalance, recipientDiscountTokenBalance,
amount_1);
uint256 discountPercent_1 = getDiscountPercent(e, sender);
uint256 amount_2;
updateDiscountDistribution(e, sender, recipient,
senderDiscountTokenBalance, recipientDiscountTokenBalance,
amount_2) at initState;
uint256 discountPercent_2 = getDiscountPercent(e, sender);
satisfy (discountPercent_1 != discountPercent_2);
}
/*================================================================================
When calling updateDiscountDistribution, the argument 'amount' has influence
on the discountPercent of the recipient.
(This catches mutant 10)
=================================================================================*/
rule in_updateDiscountDistribution_amount_affects_recpient_discount() {
env e;
address sender; address recipient;
uint256 senderDiscountTokenBalance; uint256 recipientDiscountTokenBalance; uint256 amount_1;
storage initState = lastStorage;
updateDiscountDistribution(e, sender, recipient,
senderDiscountTokenBalance, senderDiscountTokenBalance,
amount_1);
uint256 discountPercent_1 = getDiscountPercent(e, recipient);
uint256 amount_2;
updateDiscountDistribution(e, sender, recipient,
senderDiscountTokenBalance, senderDiscountTokenBalance,
amount_2) at initState;
uint256 discountPercent_2 = getDiscountPercent(e, recipient);
satisfy (discountPercent_1 != discountPercent_2);
}
/*================================================================================
After calling to setAToken, _ghoAToken!=0
(This catches mutant 4)
=================================================================================*/
rule _ghoAToken_cant_be_zero() {
env e;
address a;
setAToken(e, a);
assert get_ghoAToken() != 0;
}
rule discount_takes_place_in_burn() {
env e;
address user; uint256 amount; uint256 index;
mathint _amountScaled = rayDiv(e,amount,index);
mathint _prev_scaledBal = scaledBalanceOf(e, user);
mathint _prev_bal = balanceOf(e, user);
uint256 _discountScaled = get_discount_scaled(e,user,index);
burn(e,user,amount,index);
mathint _after_scaledBal = scaledBalanceOf(e, user);
assert to_mathint(amount)==_prev_bal => _after_scaledBal==0;
assert (to_mathint(amount)!=_prev_bal && _discountScaled>0) =>
_after_scaledBal < _prev_scaledBal - _amountScaled;
}
================================================
FILE: certora/gho/specs/ghoVariableDebtToken.spec
================================================
//import "erc20.spec"
import "VariableDebtToken.spec";
import "summarizations.spec";
using GhoDiscountRateStrategy as discStrategy;
methods{
/********************;
* WadRayMath.sol *;
*********************/
// function _.rayMul(uint256 x, uint256 y) internal => rayMulSummariztion(x, y) expect(uint256);
function rayDiv(uint256 x, uint256 y) external returns uint256 envfree;
function rayMul(uint256 x, uint256 y) external returns uint256 envfree;
/***********************************;
* PoolAddressesProvider.sol *;
************************************/
function _.getACLManager() external => CONSTANT;
/************************;
* ACLManager.sol *;
*************************/
function _.isPoolAdmin(address) external => CONSTANT;
/******************************************************************;
* DummyERC20WithTimedBalanceOf.sol (linked to _discountToken) *;
*******************************************************************/
// Internal function in DummyERC20WithTimedBalanceOf which exposes the block's timestamp and called by DummyERC20WithTimedBalanceOf::balanceOf
function _._balanceOfWithBlockTimestamp(address user, uint256 ts) internal => balanceOfDiscountTokenAtTimestamp(user, ts) expect uint256;
/************************************;
* DummyPool.sol (linked to POOL) *;
*************************************/
// Internal function in DummyPool which exposes the block's timestamp and called by Pool::getReserveNormalizedVariableDebt
function _._getReserveNormalizedVariableDebtWithBlockTimestamp(address asset, uint256 timestamp) internal => indexAtTimestamp(timestamp) expect uint256;
/************************************;
* GhoVariableDebtTokenHarness.sol *;
*************************************/
function discStrategy.calculateDiscountRate(uint256, uint256) external returns (uint256) envfree;
/************************************;
* GhoVariableDebtTokenHarness.sol *;
*************************************/
function getUserCurrentIndex(address) external returns (uint256) envfree;
function getUserDiscountRate(address) external returns (uint256) envfree;
function getUserAccumulatedDebtInterest(address) external returns (uint256) envfree;
function getBalanceOfDiscountToken(address) external returns (uint256);
function getDiscountToken() external returns (address) envfree;
/********************************;
* GhoVariableDebtToken.sol *;
*********************************/
function totalSupply() external returns(uint256) envfree;
function balanceOf(address) external returns (uint256);
function mint(address, address, uint256, uint256) external returns (bool, uint256);
function burn(address ,uint256 ,uint256) external returns (uint256);
function scaledBalanceOf(address) external returns (uint256) envfree;
function getBalanceFromInterest(address) external returns (uint256) envfree;
function rebalanceUserDiscountPercent(address) external;
function updateDiscountDistribution(address ,address ,uint256 ,uint256 ,uint256) external;
/********************************;
* GhoDiscountRateStrategy.sol *;
*********************************/
function discStrategy.DISCOUNT_RATE() external returns (uint256) envfree;
}
/**
* CVL implementation of rayMul
**/
function rayMulCVL(uint256 a, uint256 b) returns mathint {
return ((a * b + (ray() / 2)) / ray());
}
function rayDivCVL(uint256 a, uint256 b) returns mathint {
return ((a * ray() + (b / 2)) / b);
}
function getReserveNormalizedVariableDebt_1ray() returns mathint {
return ray();
}
function getReserveNormalizedVariableDebt_1or2ray() returns uint256 {
uint256 index;
require (index==ray() || to_mathint(index)==2*ray());
return index;
}
function getReserveNormalizedVariableDebt_7ray() returns uint256 {
uint256 index;
require (to_mathint(index)==7*ray());
return index;
}
//todo: check balanceof after mint (stable index), burn after balanceof
definition MAX_DISCOUNT() returns uint256 = 10000; // equals to 100% discount, in points
ghost mapping(address => mapping (uint256 => uint256)) discount_ghost;
ghost mapping(uint256 => uint256) index_ghost;
/**
* Query index_ghost for the index value at the input timestamp
**/
function indexAtTimestamp(uint256 timestamp) returns uint256 {
require index_ghost[timestamp] >= ray();
return index_ghost[timestamp];
// return 1001684385021630839436707910;//index_ghost[timestamp];
}
/**
* Query discount_ghost for the [user]'s balance of discount token at [timestamp]
**/
function balanceOfDiscountTokenAtTimestamp(address user, uint256 timestamp) returns uint256 {
return discount_ghost[user][timestamp];
}
/**
* Returns an env instance with [ts] as the block timestamp
**/
function envAtTimestamp(uint256 ts) returns env {
env e;
require(e.block.timestamp == ts);
return e;
}
/**
* @title at any point in time, the user's discount rate isn't larger than 100%
**/
invariant discountCantExceed100Percent(address user)
getUserDiscountRate(user) <= MAX_DISCOUNT()
{
preserved updateDiscountDistribution(address sender,address recipient,uint256 senderDiscountTokenBalance,uint256 recipientDiscountTokenBalance,uint256 amount) with (env e) {
require(indexAtTimestamp(e.block.timestamp) >= ray());
}
}
/**
* @title at any point in time, the user's discount rate isn't larger than DISCOUNT_RATE
**/
invariant discountCantExceedDiscountRate(address user)
getUserDiscountRate(user) <= discStrategy.DISCOUNT_RATE()
{
preserved updateDiscountDistribution(address sender,address recipient,uint256 senderDiscountTokenBalance,uint256 recipientDiscountTokenBalance,uint256 amount) with (env e) {
require(indexAtTimestamp(e.block.timestamp) >= ray());
}
}
// mutant 6
// A new discount token is not address zero
rule nonzeroNewDiscountToken{
env e;
address newDiscountToken;
updateDiscountToken(e, newDiscountToken);
assert newDiscountToken != 0;
}
// If a user's index has changed then it is assigned with the current pool index.
// Assuming that the Pool calls mint() and burn() with its current index.
invariant user_index_up_to_date(env e1, address user1)
scaledBalanceOf(e1, user1) != 0 =>
getUserCurrentIndex(user1) == indexAtTimestamp(e1.block.timestamp)
{
preserved mint(address user2, address onBehalfOf, uint256 amount, uint256 index) with (env e2)
{
require index == indexAtTimestamp(e2.block.timestamp);
require e1.block.timestamp == e2.block.timestamp;
}
preserved burn(address from, uint256 amount, uint256 index) with (env e3)
{
require index == indexAtTimestamp(e3.block.timestamp);
require e1.block.timestamp == e3.block.timestamp;
}
preserved with (env e4)
{
require e1.block.timestamp == e4.block.timestamp;
}
}
// check user index after mint()
rule user_index_after_mint
{
env e;
address user;
address onBehalfOf;
uint256 amount;
uint256 index;
uint256 user_index_before = getUserCurrentIndex(onBehalfOf);
mint(e, user, onBehalfOf, amount, index);
uint256 user_index_after = getUserCurrentIndex(onBehalfOf);
assert index > user_index_before => user_index_after > user_index_before;
assert user_index_after == index;
}
// check accumulated interest after mint()
rule accumulated_interest_increase_after_mint
{
env e;
address user;
address onBehalfOf;
uint256 amount;
uint256 index;
requireInvariant user_index_ge_one_ray(e, onBehalfOf);
requireInvariant discountCantExceedDiscountRate(onBehalfOf);
uint256 user_index_before = getUserCurrentIndex(onBehalfOf);
uint256 balance_before = balanceOf(e, onBehalfOf);
uint256 discount_before = getUserDiscountRate(onBehalfOf);
uint256 accumulated_interest_before = getUserAccumulatedDebtInterest(onBehalfOf);
mint(e, user, onBehalfOf, amount, index);
uint256 accumulated_interest_after = getUserAccumulatedDebtInterest(onBehalfOf);
assert balance_before > 0 && to_mathint(user_index_before + ray()) < to_mathint(index)
=> accumulated_interest_after > accumulated_interest_before;
}
// User index >= 1 ray for every user with positive balance
invariant user_index_ge_one_ray(env e1, address user1)
scaledBalanceOf(e1, user1) != 0 => ray() <= getUserCurrentIndex(user1)
{
preserved mint(address user2, address onBehalfOf, uint256 amount, uint256 index) with (env e2)
{
require index >= ray(); //TODO: verify - the Pool calls mint() with index >= 1 ray
}
preserved burn(address from, uint256 amount, uint256 index) with (env e3)
{
require index >= ray(); //TODO: verify - the Pool calls burn() with index >= 1 ray
}
}
/**
* Imported rules from VariableDebtToken.spec
**/
//pass
use rule disallowedFunctionalities;
/**
* @title proves that a user's discount rate can be updated only by calling rebalanceUserDiscountPercent
* This rule fails since updateDiscountDistribution, mint and burn can recalculate and update the user discount rate
**/
// rule onlyRebalanceCanUpdateUserDiscountRate(method f) {
// address user;
// uint256 discRateBefore = getUserDiscountRate(user);
// requireInvariant discountCantExceed100Percent(user);
// env e;
// calldataarg args;
// f(e,args);
// uint256 discRateAfter = getUserDiscountRate(user);
// assert(discRateAfter != discRateBefore => f.selector == sig:rebalanceUserDiscountPercent(address).selector);
// }
/**
* @title proves that the user's balance of debt token (as reported by GhoVariableDebtToken::balanceOf) can't increase by calling any external non-mint function.
**/
//pass
rule nonMintFunctionCantIncreaseBalance(method f) filtered { f-> f.selector != sig:mint(address, address, uint256, uint256).selector } {
address user;
uint256 ts1;
uint256 ts2;
require(ts2 >= ts1);
// Forcing the index to be fixed (otherwise the rule times out). For non-fixed index replace `==` with `>=`
require((indexAtTimestamp(ts1) >= ray()) &&
(indexAtTimestamp(ts2) == indexAtTimestamp(ts1)));
require(getUserCurrentIndex(user) == indexAtTimestamp(ts1));
requireInvariant discountCantExceed100Percent(user);
env e = envAtTimestamp(ts2);
uint256 balanceBeforeOp = balanceOf(e, user);
calldataarg args;
f(e,args);
mathint balanceAfterOp = balanceOf(e, user);
mathint allowedDiff = indexAtTimestamp(ts2) / ray();
// assert(balanceAfterOp != balanceBeforeOp + allowedDiff + 1);
assert(balanceAfterOp <= balanceBeforeOp + allowedDiff);
}
/**
* @title proves that a call to a non-mint operation won't increase the user's balance of the actual debt tokens (i.e. it's scaled balance)
**/
// pass
rule nonMintFunctionCantIncreaseScaledBalance(method f) filtered { f-> f.selector != sig:mint(address, address, uint256, uint256).selector } {
address user;
uint256 ts1;
uint256 ts2;
require(ts2 >= ts1);
require((indexAtTimestamp(ts1) >= ray()) &&
(indexAtTimestamp(ts2) >= indexAtTimestamp(ts1)));
require(getUserCurrentIndex(user) == indexAtTimestamp(ts1));
requireInvariant discountCantExceed100Percent(user);
uint256 balanceBeforeOp = scaledBalanceOf(user);
env e = envAtTimestamp(ts2);
calldataarg args;
f(e,args);
uint256 balanceAfterOp = scaledBalanceOf(user);
assert(balanceAfterOp <= balanceBeforeOp);
}
/**
* @title proves that debt tokens aren't transferable
**/
// pass
rule debtTokenIsNotTransferable(method f) {
address user1;
address user2;
require(user1 != user2);
uint256 scaledBalanceBefore1 = scaledBalanceOf(user1);
uint256 scaledBalanceBefore2 = scaledBalanceOf(user2);
env e;
calldataarg args;
f(e,args);
uint256 scaledBalanceAfter1 = scaledBalanceOf(user1);
uint256 scaledBalanceAfter2 = scaledBalanceOf(user2);
assert( scaledBalanceBefore1 + scaledBalanceBefore2 == scaledBalanceAfter1 + scaledBalanceAfter2
=> (scaledBalanceBefore1 == scaledBalanceAfter1 && scaledBalanceBefore2 == scaledBalanceAfter2));
}
/**
* @title proves that only burn/mint/rebalanceUserDiscountPercent/updateDiscountDistribution can modify user's scaled balance
**/
// pass
rule onlyCertainFunctionsCanModifyScaledBalance(method f) {
address user;
uint256 ts1;
uint256 ts2;
require(ts2 >= ts1);
require((indexAtTimestamp(ts1) >= ray()) &&
(indexAtTimestamp(ts2) >= indexAtTimestamp(ts1)));
require(getUserCurrentIndex(user) == indexAtTimestamp(ts1));
requireInvariant discountCantExceed100Percent(user);
uint256 balanceBeforeOp = scaledBalanceOf(user);
env e = envAtTimestamp(ts2);
calldataarg args;
f(e,args);
uint256 balanceAfterOp = scaledBalanceOf(user);
assert(balanceAfterOp != balanceBeforeOp => (
(f.selector == sig:mint(address ,address ,uint256 ,uint256).selector) ||
(f.selector == sig:burn(address ,uint256 ,uint256).selector) ||
(f.selector == sig:updateDiscountDistribution(address ,address ,uint256 ,uint256 ,uint256).selector) ||
(f.selector == sig:rebalanceUserDiscountPercent(address).selector)));
}
/**
* @title proves that only a call to decreaseBalanceFromInterest will decrease the user's accumulated interest listing.
**/
// pass
rule userAccumulatedDebtInterestWontDecrease(method f) {
address user;
uint256 ts1;
uint256 ts2;
require(ts2 >= ts1);
require((indexAtTimestamp(ts1) >= ray()) &&
(indexAtTimestamp(ts2) >= indexAtTimestamp(ts1)));
require(getUserCurrentIndex(user) == indexAtTimestamp(ts1));
requireInvariant discountCantExceed100Percent(user);
uint256 initAccumulatedInterest = getUserAccumulatedDebtInterest(user);
env e2 = envAtTimestamp(ts2);
calldataarg args;
f(e2,args);
uint256 finAccumulatedInterest = getUserAccumulatedDebtInterest(user);
assert(initAccumulatedInterest > finAccumulatedInterest => f.selector == sig:decreaseBalanceFromInterest(address, uint256).selector);
}
/**
* @title proves that a user can't nullify its debt without calling burn
**/
// pass
rule userCantNullifyItsDebt(method f) {
address user;
env e;
env e2;
require(getUserCurrentIndex(user) == indexAtTimestamp(e.block.timestamp));
requireInvariant discountCantExceed100Percent(user);
uint256 balanceBeforeOp = balanceOf(e, user);
calldataarg args;
require e2.block.timestamp == e.block.timestamp;
f(e2,args);
uint256 balanceAfterOp = balanceOf(e, user);
assert((balanceBeforeOp > 0 && balanceAfterOp == 0) => (f.selector == sig:burn(address, uint256, uint256).selector));
}
/***************************************************************
* Integrity of Mint
***************************************************************/
/**
* @title proves that after calling mint, the user's discount rate is up to date
**/
rule integrityOfMint_updateDiscountRate() {
address user1;
address user2;
env e;
uint256 amount;
uint256 index = indexAtTimestamp(e.block.timestamp);
mint(e, user1, user2, amount, index);
uint256 debtBalance = balanceOf(e, user2);
uint256 discountBalance = getBalanceOfDiscountToken(e, user2);
uint256 discountRate = getUserDiscountRate(user2);
assert(discStrategy.calculateDiscountRate(debtBalance, discountBalance) == discountRate);
}
/**
* @title proves the after calling mint, the user's state is updated with the recent index value
**/
rule integrityOfMint_updateIndex() {
address user1;
address user2;
env e;
uint256 amount;
uint256 index;
mint(e, user1, user2, amount, index);
assert(getUserCurrentIndex(user2) == index);
}
/**
* @title proves that on a fixed index calling mint(user, amount) will increase the user's scaled balance by amount.
**/
// pass
rule integrityOfMint_updateScaledBalance_fixedIndex() {
address user;
env e;
uint256 balanceBefore = balanceOf(e, user);
uint256 scaledBalanceBefore = scaledBalanceOf(user);
address caller;
uint256 amount;
uint256 index = indexAtTimestamp(e.block.timestamp);
require(getUserCurrentIndex(user) == index);
mint(e, caller, user, amount, index);
uint256 balanceAfter = balanceOf(e, user);
mathint scaledBalanceAfter = scaledBalanceOf(user);
mathint scaledAmount = rayDivCVL(amount, index);
assert(scaledBalanceAfter == scaledBalanceBefore + scaledAmount);
}
/**
* @title proves that mint can't effect other user's scaled balance
**/
// pass
rule integrityOfMint_userIsolation() {
address otherUser;
uint256 scaledBalanceBefore = scaledBalanceOf(otherUser);
env e;
uint256 amount;
uint256 index;
address targetUser;
address caller;
mint(e, caller, targetUser, amount, index);
uint256 scaledBalanceAfter = scaledBalanceOf(otherUser);
assert(scaledBalanceAfter != scaledBalanceBefore => otherUser == targetUser);
}
/**
* @title proves that when calling mint, the user's balance (as reported by GhoVariableDebtToken::balanceOf) will increase if the call is made on bahalf of the user.
**/
rule onlyMintForUserCanIncreaseUsersBalance() {
address user1;
env e;
require(getUserCurrentIndex(user1) == indexAtTimestamp(e.block.timestamp));
uint256 finBalanceBeforeMint = balanceOf(e, user1);
uint256 amount;
mint(e,user1, user1, amount, indexAtTimestamp(e.block.timestamp));
uint256 finBalanceAfterMint = balanceOf(e, user1);
assert(finBalanceAfterMint != finBalanceBeforeMint);
}
/**
* @title proves that a user can't decrease the ovelall interest of his position by taking more loans, compared to another user with the same initial position.
* This rule times out.
**/
// rule integrityOfMint_cantDecreaseInterestWithMint() {
// address user1;
// uint256 ts1;
// env e1 = envAtTimestamp(ts1);
// uint256 ts2;
// require(ts2 >= ts1);
// env e2 = envAtTimestamp(ts2);
// uint256 ts3;
// require(ts3 >= ts2);
// env e3 = envAtTimestamp(ts3);
// // Forcing the index to be fixed (otherwise the rule times out). For non-fixed index replace `==` with `>=`
// require((indexAtTimestamp(ts1) >= ray()) &&
// (indexAtTimestamp(ts2) >= indexAtTimestamp(ts1)) &&
// (indexAtTimestamp(ts3) >= indexAtTimestamp(ts2)));
// require(getUserCurrentIndex(user1) == indexAtTimestamp(ts1));
// uint256 amount;
// storage initialStorage = lastStorage;
// mint(e2, user1, user1, amount, indexAtTimestamp(ts2));
// rebalanceUserDiscountPercent(e3, user1);
// uint256 balanceFromInterestAfterMint = getBalanceFromInterest(user1);
// rebalanceUserDiscountPercent(e3, user1) at initialStorage;
// uint256 balanceFromInterestWithoutMint = getBalanceFromInterest(user1);
// assert(balanceFromInterestAfterMint >= balanceFromInterestWithoutMint);
// }
//pass
use rule integrityMint_atoken;
/***************************************************************
* Integrity of Burn
***************************************************************/
/**
* @title proves that after calling burn, the user's discount rate is up to date
**/
rule integrityOfBurn_updateDiscountRate() {
address user;
env e;
uint256 amount;
uint256 index = indexAtTimestamp(e.block.timestamp);
burn(e, user, amount, index);
uint256 debtBalance = balanceOf(e, user);
uint256 discountBalance = getBalanceOfDiscountToken(e, user);
uint256 discountRate = getUserDiscountRate(user);
assert(discStrategy.calculateDiscountRate(debtBalance, discountBalance) == discountRate);
}
/**
* @title proves the after calling burn, the user's state is updated with the recent index value
**/
rule integrityOfBurn_updateIndex() {
address user;
env e;
uint256 amount;
uint256 index;
burn(e, user, amount, index);
assert(getUserCurrentIndex(user) == index);
}
/**
* @title proves that calling burn with 0 amount doesn't change the user's balance
**/
use rule burnZeroDoesntChangeBalance;
/**
* @title proves a concrete case of repaying the full debt that ends with a zero balance
**/
rule integrityOfBurn_fullRepay_concrete() {
env e;
address user;
uint256 currentDebt = balanceOf(e, user);
uint256 index = indexAtTimestamp(e.block.timestamp);
require(getUserCurrentIndex(user) == ray());
require(to_mathint(index) == 2*ray());
require(to_mathint(scaledBalanceOf(user)) == 4*ray());
burn(e, user, currentDebt, index);
uint256 scaled = scaledBalanceOf(user);
assert(scaled == 0);
}
/**
* @title proves that burn can't effect other user's scaled balance
**/
// pass
rule integrityOfBurn_userIsolation() {
address otherUser;
uint256 scaledBalanceBefore = scaledBalanceOf(otherUser);
env e;
uint256 amount;
uint256 index;
address targetUser;
burn(e,targetUser, amount, index);
uint256 scaledBalanceAfter = scaledBalanceOf(otherUser);
assert(scaledBalanceAfter != scaledBalanceBefore => otherUser == targetUser);
}
/***************************************************************
* Integrity of updateDiscountDistribution
***************************************************************/
// /**
// * @title proves that the discount rate is calculated correctly when calling updateDiscountDistribution
// **/
// rule integrityOfUpdateDiscountDistribution_discountRate() {
// address sender;
// address recipient;
// uint256 senderDiscountTokenBalanceBefore;
// uint256 recipientDiscountTokenBalanceBefore;
// uint256 amount;
// uint256 senderDiscountTokenBalanceAfter = require_uint256(senderDiscountTokenBalanceBefore - amount);
// uint256 recipientDiscountTokenBalanceAfter = require_uint256(recipientDiscountTokenBalanceBefore + amount);
// env e0;
// env e;
// require(e.block.timestamp > e0.block.timestamp);
// require(indexAtTimestamp(e.block.timestamp) >= indexAtTimestamp(e0.block.timestamp));
// require(indexAtTimestamp(e0.block.timestamp) == ray()); // reduces execution time
// require(getUserCurrentIndex(sender) == indexAtTimestamp(e0.block.timestamp));
// require(getUserCurrentIndex(recipient) == indexAtTimestamp(e0.block.timestamp));
// require(getBalanceOfDiscountToken(e0, sender) == senderDiscountTokenBalanceBefore);
// require(getBalanceOfDiscountToken(e0, recipient) == recipientDiscountTokenBalanceBefore);
// require(discStrategy.calculateDiscountRate(balanceOf(e0, sender), senderDiscountTokenBalanceBefore) == getUserDiscountRate(sender));
// require(discStrategy.calculateDiscountRate(balanceOf(e0, recipient), recipientDiscountTokenBalanceBefore) == getUserDiscountRate(recipient));
// require(getBalanceOfDiscountToken(e, sender) == senderDiscountTokenBalanceAfter);
// require(getBalanceOfDiscountToken(e, recipient) == recipientDiscountTokenBalanceAfter);
// updateDiscountDistribution(e, sender, recipient, senderDiscountTokenBalanceBefore, recipientDiscountTokenBalanceBefore, amount);
// uint256 senderBalance = balanceOf(e, sender);
// uint256 recipientBalance = balanceOf(e, recipient);
// assert(discStrategy.calculateDiscountRate(senderBalance, senderDiscountTokenBalanceAfter) == getUserDiscountRate(sender));
// assert(discStrategy.calculateDiscountRate(recipientBalance, recipientDiscountTokenBalanceAfter) == getUserDiscountRate(recipient));
// }
rule sendersDiscountPercentCannotIncrease(){
env e1;
address sender; address recipient; uint256 amount;
uint256 _senderStkBalance = getBalanceOfDiscountToken(e1, sender);
uint256 _recipientStkBalance = getBalanceOfDiscountToken(e1, recipient);
uint256 indE1 = indexAtTimestamp(e1.block.timestamp);
// require(indE1 >= ray()); // this is already enforced in the funciton's body
require getUserCurrentIndex(sender) == indE1;
uint256 _sender_debt = balanceOf(e1, sender);
uint256 discount_sender = discStrategy.calculateDiscountRate(_sender_debt, _senderStkBalance);
require(discount_sender == getDiscountPercent(e1, sender));
require discount_sender != 0; // this can be violated due to discontinuity of calculateDiscountRate
env e2;
require e1.block.timestamp <= e2.block.timestamp;
uint256 indE2 = indexAtTimestamp(e2.block.timestamp);
require(indE2 >= indE1);
require _senderStkBalance == getBalanceOfDiscountToken(e2, sender);
require _recipientStkBalance == getBalanceOfDiscountToken(e2, recipient);
updateDiscountDistribution(e2, sender, recipient, _senderStkBalance, _recipientStkBalance, amount);
uint256 discountPercent_ = getDiscountPercent(e2, sender);
assert (discountPercent_ <= discount_sender);
}
/**
* @title proves the after calling updateDiscountDistribution, the user's state is updated with the recent index value
**/
rule integrityOfUpdateDiscountDistribution_updateIndex() {
address sender;
address recipient;
uint256 senderDiscountTokenBalance;
uint256 recipientDiscountTokenBalance;
env e;
uint256 amount;
uint256 _senderInd = getUserCurrentIndex(sender);
uint256 _recipientInd = getUserCurrentIndex(recipient);
uint256 index = indexAtTimestamp(e.block.timestamp);
updateDiscountDistribution(e, sender, recipient, senderDiscountTokenBalance, recipientDiscountTokenBalance, amount);
if (sender != recipient){
assert(scaledBalanceOf(sender) > 0 => getUserCurrentIndex(sender) == index);
assert(scaledBalanceOf(recipient) > 0 => getUserCurrentIndex(recipient) == index);
}
else{
assert(getUserCurrentIndex(sender) == _senderInd);
assert(getUserCurrentIndex(recipient) == _recipientInd); // this is redundant, this is here for future changes in the code/rule
}
}
/**
* @title proves that updateDiscountDistribution can't effect other user's scaled balance
**/
// pass
rule integrityOfUpdateDiscountDistribution_userIsolation() {
address otherUser;
uint256 scaledBalanceBefore = scaledBalanceOf(otherUser);
env e;
uint256 amount;
uint256 senderDiscountTokenBalance;
uint256 recipientDiscountTokenBalance;
address sender;
address recipient;
updateDiscountDistribution(e, sender, recipient, senderDiscountTokenBalance, recipientDiscountTokenBalance, amount);
uint256 scaledBalanceAfter = scaledBalanceOf(otherUser);
assert(scaledBalanceAfter != scaledBalanceBefore => (otherUser == sender || otherUser == recipient));
}
/***************************************************************
* Integrity of rebalanceUserDiscountPercent
***************************************************************/
/**
* @title proves that after calling rebalanceUserDiscountPercent, the user's discount rate is up to date
**/
rule integrityOfRebalanceUserDiscountPercent_updateDiscountRate() {
address user;
env e;
rebalanceUserDiscountPercent(e, user);
assert(discStrategy.calculateDiscountRate(balanceOf(e, user), getBalanceOfDiscountToken(e, user)) == getUserDiscountRate(user));
}
/**
* @title proves the after calling rebalanceUserDiscountPercent, the user's state is updated with the recent index value
**/
rule integrityOfRebalanceUserDiscountPercent_updateIndex() {
address user;
env e;
rebalanceUserDiscountPercent(e, user);
uint256 index = indexAtTimestamp(e.block.timestamp);
assert(getUserCurrentIndex(user) == index);
}
/**
* @title proves that rebalanceUserDiscountPercent can't effect other user's scaled balance
**/
// pass
rule integrityOfRebalanceUserDiscountPercent_userIsolation() {
address otherUser;
uint256 scaledBalanceBefore = scaledBalanceOf(otherUser);
env e;
address targetUser;
rebalanceUserDiscountPercent(e, targetUser);
uint256 scaledBalanceAfter = scaledBalanceOf(otherUser);
assert(scaledBalanceAfter != scaledBalanceBefore => otherUser == targetUser);
}
/***************************************************************
* Integrity of balanceOf
***************************************************************/
/**
* @title proves that a user with 100% discounts has a fixed balance over time
**/
rule integrityOfBalanceOf_fullDiscount() {
address user;
uint256 fullDiscountRate = 10000; //100%
require(getUserDiscountRate(user) == fullDiscountRate);
env e1;
env e2;
uint256 index1 = indexAtTimestamp(e1.block.timestamp);
uint256 index2 = indexAtTimestamp(e2.block.timestamp);
assert(balanceOf(e1, user) == balanceOf(e2, user));
}
/**
* @title proves that a user's balance, with no discount, is equal to rayMul(scaledBalance, current index)
**/
rule integrityOfBalanceOf_noDiscount() {
address user;
require(getUserDiscountRate(user) == 0);
env e;
uint256 scaledBalance = scaledBalanceOf(user);
uint256 currentIndex = indexAtTimestamp(e.block.timestamp);
mathint expectedBalance = rayMulCVL(scaledBalance, currentIndex);
assert(to_mathint(balanceOf(e, user)) == expectedBalance);
}
/**
* @title proves the a user with zero scaled balance has a zero balance
**/
rule integrityOfBalanceOf_zeroScaledBalance() {
address user;
env e;
uint256 scaledBalance = scaledBalanceOf(user);
require(scaledBalance == 0);
assert(balanceOf(e, user) == 0);
}
/**
* @title burning amount of current debt nullifies the debt position
**/
rule burnAllDebtReturnsZeroDebt(address user) {
env e;
uint256 _variableDebt = balanceOf(e, user);
burn(e, user, _variableDebt, indexAtTimestamp(e.block.timestamp));
uint256 variableDebt_ = balanceOf(e, user);
assert(variableDebt_ == 0);
}
/**
* @title discount strategy setter is setting the corresponding storage slot to the passed value
**/
rule integrityOfUpdateDiscountRateStrategy(address newDiscountRateStrategy) {
env e;
updateDiscountRateStrategy(e, newDiscountRateStrategy );
assert(getDiscountRateStrategy(e) == newDiscountRateStrategy);
}
================================================
FILE: certora/gho/specs/ghoVariableDebtTokenInternal.spec
================================================
import "ghoVariableDebtToken.spec";
methods{
}
// check a scenario that function _accrueDebtOnAction() returns non zero balance increase
rule positive_balanceIncrease {
env e;
address user;
uint256 previousScaledBalance; uint256 discountPercent; uint256 index;
uint256 balanceIncrease; uint256 discountScaled;
uint256 user_index_before = getUserCurrentIndex(user);
uint256 accumulated_interest_before = getUserAccumulatedDebtInterest(user);
balanceIncrease, discountScaled = accrueDebtOnAction(e, user,previousScaledBalance,discountPercent,index);
uint256 accumulated_interest_after = getUserAccumulatedDebtInterest(user);
uint256 user_index_after = getUserCurrentIndex(user);
assert ray() <= user_index_before
&& to_mathint(user_index_before + ray()) < to_mathint(index) // user index increase by more than 1 ray
&& 0 < previousScaledBalance
&& discountPercent < discStrategy.DISCOUNT_RATE() // discount rate is less than 30%
//(if user index increases by 1 ray discount percent could be as high as 50%)
=> balanceIncrease > 0;
assert balanceIncrease > 0 => accumulated_interest_after > accumulated_interest_before;
assert user_index_after == index;
}
================================================
FILE: certora/gho/specs/ghoVariableDebtToken_summarized.spec
================================================
import "ghoVariableDebtToken.spec";
methods{
function GhoVariableDebtToken._accrueDebtOnAction(address user, uint256, uint256, uint256) internal returns (uint256, uint256) => flipAccrueCalled(user);
function GhoVariableDebtToken._refreshDiscountPercent(address user, uint256, uint256, uint256) internal => flipRefreshCalled(user);
}
ghost mapping(address => mathint) accrue_called_counter {
init_state axiom forall address user. accrue_called_counter[user] == 0;
}
ghost mapping(address => mathint) refresh_called_counter {
init_state axiom forall address user. refresh_called_counter[user] == 0;
}
function flipAccrueCalled(address user) returns (uint256, uint256) {
accrue_called_counter[user] = accrue_called_counter[user] + 1;
return (0, 0);
}
function flipRefreshCalled(address user) {
// before refreshing a user, accrue of the user should've been called exactly once
// of course calling accrue twice is not a crucial mistake, but accruing the same user twice in a row before refreshing doesn't make sense, so a violation should be triggered
assert(refresh_called_counter[user] + 1 == accrue_called_counter[user]);
refresh_called_counter[user] = refresh_called_counter[user] + 1;
}
invariant allUsersRefreshAndAccrueCounterEqual()
forall address user. accrue_called_counter[user] == refresh_called_counter[user];
// accrue is always called before refresh
rule accrueAlwaysCalleldBeforeRefresh(env e, method f) {
address user1;
requireInvariant allUsersRefreshAndAccrueCounterEqual();
// require (forall address user. (accrue_called_counter[user] == refresh_called_counter[user]));
calldataarg args;
// see comment in flipRefreshCalled
f(e, args);
assert refresh_called_counter[user1] == accrue_called_counter[user1], "Remember, with great power comes great responsibility.";
}
// accrue is always called before refresh example
// should pass only on updateDiscountDistribution
rule accrueAlwaysCalledBeforeRefresh_witness(env e, method f) {
address user1;
mathint counter = accrue_called_counter[user1];
require accrue_called_counter[user1] == refresh_called_counter[user1];
calldataarg args;
f(e, args);
satisfy(refresh_called_counter[user1] == counter + 2);
}
================================================
FILE: certora/gho/specs/set-natspec.json
================================================
[
{
"content": "/**\n * @title get Set array length\n * @dev user should define getLen() in Solidity harness file.\n */\nmethods{\n getLen() returns (uint256) envfree\n}",
"type": "methods",
"title": "get Set array length",
"dev": "user should define getLen() in Solidity harness file."
},
{
"content": "/**\n* @title max uint256\n* @return 2^256-1\n*/\ndefinition MAX_UINT256() returns uint256 = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;",
"type": "definition",
"id": "MAX_UINT256",
"title": "max uint256",
"return": {
"type": "uint256"
}
},
{
"content": "/**\n* @title max address value + 1\n* @returns 2^160\n*/\ndefinition TWO_TO_160() returns uint256 = 0x10000000000000000000000000000000000000000;",
"type": "definition",
"id": "TWO_TO_160",
"title": "max address value + 1",
"return": {
"type": "uint256"
}
},
{
"content": "/**\n* @title Set map entries point to valid array entries\n* @notice an essential condition of the set, should hold for evert Set implementation \n* @return true if all map entries points to valid indexes of the array.\n*/\ndefinition MAP_POINTS_INSIDE_ARRAY() returns bool = forall bytes32 a. mirrorMap(a) <= mirrorArrayLen();",
"type": "definition",
"id": "MAP_POINTS_INSIDE_ARRAY",
"title": "Set map entries point to valid array entries",
"return": {
"type": "bool",
"comment": "true if all map entries points to valid indexes of the array."
},
"notice": "an essential condition of the set, should hold for evert Set implementation"
},
{
"content": "/**\n* @title Set map is the inverse function of set array. \n* @notice an essential condition of the set, should hold for evert Set implementation \n* @notice this condition depends on the other set conditions, but the other conditions do not depend on this condition.\n* If this condition is omitted the rest of the conditions still hold, but the other conditions are required to prove this condition.\n* @return true if for every valid index of the array it holds that map(array(index)) == index + 1.\n*/\ndefinition MAP_IS_INVERSE_OF_ARRAY() returns bool = forall uint256 i. (i < mirrorArrayLen()) => (mirrorMap(mirrorArray(i))) == to_uint256(i + 1);",
"type": "definition",
"id": "MAP_IS_INVERSE_OF_ARRAY",
"title": "Set map is the inverse function of set array.",
"return": {
"type": "bool"
},
"notice": "this condition depends on the other set conditions, but the other conditions do not depend on this condition.\nIf this condition is omitted the rest of the conditions still hold, but the other conditions are required to prove this condition."
},
{
"content": "/**\n* @title Set array is the inverse function of set map\n* @notice an essential condition of the set, should hold for evert Set implementation \n* @return true if for every non-zero bytes32 value stored in in the set map it holds that array(map(value) - 1) == value\n*/\ndefinition ARRAY_IS_INVERSE_OF_MAP() returns bool = forall bytes32 a. (mirrorMap(a) != 0) => (mirrorArray(to_uint256(mirrorMap(a)-1)) == a);",
"type": "definition",
"id": "ARRAY_IS_INVERSE_OF_MAP",
"title": "Set array is the inverse function of set map",
"return": {
"type": "bool",
"comment": "true if for every non-zero bytes32 value stored in in the set map it holds that array(map(value) - 1) == value"
},
"notice": "an essential condition of the set, should hold for evert Set implementation"
},
{
"content": "/**\n* @title load array length\n* @notice a dummy condition that forces load of array length, using it forces initialization of mirrorArrayLen()\n* @return always true\n*/\ndefinition CVL_LOAD_ARRAY_LENGTH() returns bool = (getLen() == getLen());",
"type": "definition",
"id": "CVL_LOAD_ARRAY_LENGTH",
"title": "load array length",
"return": {
"type": "bool",
"comment": "always true"
},
"notice": "a dummy condition that forces load of array length, using it forces initialization of mirrorArrayLen()"
},
{
"content": "/**\n* @title Set-general condition, encapsulating all conditions of Set \n* @notice this condition recaps the general characteristics of Set. It should hold for all set implementations i.e. AddressSet, UintSet, Bytes32Set\n* @return conjunction of the Set three essential properties.\n*/\ndefinition SET_INVARIANT() returns bool = MAP_POINTS_INSIDE_ARRAY() && MAP_IS_INVERSE_OF_ARRAY() && ARRAY_IS_INVERSE_OF_MAP() && CVL_LOAD_ARRAY_LENGTH();",
"type": "definition",
"id": "SET_INVARIANT",
"title": "Set-general condition, encapsulating all conditions of Set",
"return": {
"type": "bool"
},
"notice": "this condition recaps the general characteristics of Set. It should hold for all set implementations i.e. AddressSet, UintSet, Bytes32Set"
},
{
"content": "/**\n * @title Size of stored value does not exceed the size of an address type.\n * @notice must be used for AddressSet, must not be used for Bytes32Set, UintSet\n * @return true if all array entries are less than 160 bits.\n **/\ndefinition VALUE_IN_BOUNDS_OF_TYPE_ADDRESS() returns bool = (forall uint256 i. to_uint256(mirrorArray(i)) < TWO_TO_160());",
"type": "definition",
"id": "VALUE_IN_BOUNDS_OF_TYPE_ADDRESS",
"title": "Size of stored value does not exceed the size of an address type.",
"return": {
"type": "bool",
"comment": "true if all array entries are less than 160 bits."
},
"notice": "must be used for AddressSet, must not be used for Bytes32Set, UintSet"
},
{
"content": "/**\n * @title A complete invariant condition for AddressSet\n * @notice invariant addressSetInvariant proves that this condition holds\n * @return conjunction of the Set-general and AddressSet-specific conditions\n **/\ndefinition ADDRESS_SET_INVARIANT() returns bool = SET_INVARIANT() && VALUE_IN_BOUNDS_OF_TYPE_ADDRESS();",
"type": "definition",
"id": "ADDRESS_SET_INVARIANT",
"title": "A complete invariant condition for AddressSet",
"return": {
"type": "bool",
"comment": "conjunction of the Set-general and AddressSet-specific conditions"
},
"notice": "invariant addressSetInvariant proves that this condition holds"
},
{
"content": "/**\n * @title A complete invariant condition for UintSet, Bytes32Set\n * @notice for UintSet and Bytes2St no type-specific condition is required because the type size is the same as the native type (bytes32) size\n * @return the Set-general condition\n **/\ndefinition UINT_SET_INVARIANT() returns bool = SET_INVARIANT();",
"type": "definition",
"id": "UINT_SET_INVARIANT",
"title": "A complete invariant condition for UintSet, Bytes32Set",
"return": {
"type": "bool",
"comment": "the Set-general condition"
},
"notice": "for UintSet and Bytes2St no type-specific condition is required because the type size is the same as the native type (bytes32) size"
},
{
"content": "/**\n * @title Out of bound array entries are zero\n * @notice A non-essential condition. This condition can be proven as an invariant, but it is not necessary for proving the Set correctness.\n * @return true if all entries beyond array length are zero\n **/\ndefinition ARRAY_OUT_OF_BOUND_ZERO() returns bool = forall uint256 i. (i >= mirrorArrayLen()) => (mirrorArray(i) == 0);",
"type": "definition",
"id": "ARRAY_OUT_OF_BOUND_ZERO",
"title": "Out of bound array entries are zero",
"return": {
"type": "bool",
"comment": "true if all entries beyond array length are zero"
},
"notice": "A non-essential condition. This condition can be proven as an invariant, but it is not necessary for proving the Set correctness."
},
{
"content": "/**\n * @title ghost mirror map, mimics Set map\n **/\nghost mirrorMap(bytes32) returns uint256{\n init_state axiom forall bytes32 a. mirrorMap(a) == 0;\n}",
"type": "ghost",
"id": "mirrorMap",
"title": "ghost mirror map, mimics Set map",
"return": {
"type": "uint256"
}
},
{
"content": "/**\n * @title ghost mirror array, mimics Set array\n **/\nghost mirrorArray(uint256) returns bytes32{\n init_state axiom forall uint256 i. mirrorArray(i) == 0;\n}",
"type": "ghost",
"id": "mirrorArray",
"title": "ghost mirror array, mimics Set array",
"return": {
"type": "bytes32"
}
},
{
"content": "/**\n * @title ghost mirror array length, mimics Set array length\n * @notice ghost includes an assumption about the array length. \n * If the assumption were not written in the ghost function it should be written in every rule and invariant.\n * The assumption holds: breaking the assumptions would violate the invariant condition 'map(array(index)) == index + 1'. Set map uses 0 as a sentinel value, so the array cannot contain MAX_INT different values. \n * The assumption is necessary: if a value is added when length==MAX_INT then length overflows and becomes zero.\n **/\nghost mirrorArrayLen() returns uint256{\n init_state axiom mirrorArrayLen() == 0;\n axiom mirrorArrayLen() != MAX_UINT256(); \n}",
"type": "ghost",
"id": "mirrorArrayLen",
"title": "ghost mirror array length, mimics Set array length",
"return": {
"type": "uint256"
},
"notice": "ghost includes an assumption about the array length.\nIf the assumption were not written in the ghost function it should be written in every rule and invariant.\nThe assumption holds: breaking the assumptions would violate the invariant condition 'map(array(index)) == index + 1'. Set map uses 0 as a sentinel value, so the array cannot contain MAX_INT different values.\nThe assumption is necessary: if a value is added when length==MAX_INT then length overflows and becomes zero."
},
{
"content": "/**\n * @title main Set general invariant\n **/\ninvariant setInvariant()\n SET_INVARIANT()",
"type": "invariant",
"id": "setInvariant",
"title": "main Set general invariant"
},
{
"content": "/**\n * @title main AddressSet invariant\n * @dev user of the spec should add 'requireInvariant addressSetInvariant();' to every rule and invariant that refer to a contract that instantiates AddressSet \n **/\ninvariant addressSetInvariant()\n ADDRESS_SET_INVARIANT()",
"type": "invariant",
"id": "addressSetInvariant",
"title": "main AddressSet invariant",
"dev": "user of the spec should add 'requireInvariant addressSetInvariant();' to every rule and invariant that refer to a contract that instantiates AddressSet"
},
{
"content": "/**\n * @title addAddress() successfully adds an address\n **/\nrule api_add_succeeded()\n{\n env e;\n address a;\n requireInvariant addressSetInvariant();\n require !contains(e, a);\n assert addAddress(e, a);\n assert contains(e, a);\n}",
"type": "rule",
"id": "api_add_succeeded",
"title": "addAddress() successfully adds an address"
},
{
"content": "/**\n * @title addAddress() fails to add an address if it already exists \n * @notice check set membership using contains()\n **/\nrule api_add_failed_contains()\n{\n env e;\n address a;\n requireInvariant addressSetInvariant();\n require contain(e, a);\n assert !addAddress(e, a);\n}",
"type": "rule",
"id": "api_add_failed_contains",
"title": "addAddress() fails to add an address if it already exists",
"notice": "check set membership using contains()"
},
{
"content": "/**\n * @title addAddress() fails to add an address if it already exists \n * @notice check set membership using atIndex()\n **/\nrule api_add_failed_at()\n{\n env e;\n address a;\n uint256 index;\n requireInvariant addressSetInvariant();\n require atIndex(e, index) == a;\n assert !addAddress(e, a);\n}",
"type": "rule",
"id": "api_add_failed_at",
"title": "addAddress() fails to add an address if it already exists",
"notice": "check set membership using atIndex()"
},
{
"content": "/**\n * @title contains() succeed after addAddress succeeded \n **/\nrule api_address_contained_affter_add()\n{\n env e;\n address a;\n requireInvariant addressSetInvariant();\n addAddress(e, a);\n assert contains(e, a);\n}",
"type": "rule",
"id": "api_address_contained_affter_add",
"title": "contains() succeed after addAddress succeeded"
},
{
"content": "/**\n * @title _removeAddress() succeeds to remove an address if it existed \n * @notice check set membership using contains()\n **/\nrule api_remove_succeeded_contains()\n{\n env e;\n address a;\n requireInvariant addressSetInvariant();\n require contains(e, a);\n assert _removeAddress(e, a);\n}",
"type": "rule",
"id": "api_remove_succeeded_contains",
"title": "_removeAddress() succeeds to remove an address if it existed",
"notice": "check set membership using contains()"
},
{
"content": "/**\n * @title _removeAddress() fails to remove address if it didn't exist \n **/\nrule api_remove_failed()\n{\n env e;\n address a;\n requireInvariant addressSetInvariant();\n require !contains(e, a);\n assert !_removeAddress(e, a);\n}",
"type": "rule",
"id": "api_remove_failed",
"title": "_removeAddress() fails to remove address if it didn't exist"
},
{
"content": "/**\n * @title _removeAddress() succeeds to remove an address if it existed \n * @notice check set membership using atIndex()\n **/\nrule api_remove_succeeded_at()\n{\n env e;\n address a;\n uint256 index;\n requireInvariant addressSetInvariant();\n require atIndex(e, index) == a;\n assert _removeAddress(e, a);\n}",
"type": "rule",
"id": "api_remove_succeeded_at",
"title": "_removeAddress() succeeds to remove an address if it existed",
"notice": "check set membership using atIndex()"
},
{
"content": "/**\n * @title contains() failed after an address was removed\n **/\nrule api_not_contains_affter_remove()\n{\n env e;\n address a;\n requireInvariant addressSetInvariant();\n _removeAddress(e, a);\n assert !contains(e, a);\n}",
"type": "rule",
"id": "api_not_contains_affter_remove",
"title": "contains() failed after an address was removed"
},
{
"content": "/**\n * @title contains() succeeds if atIndex() succeeded\n **/\nrule cover_at_contains()\n{\n env e;\n address a = 0;\n requireInvariant addressSetInvariant();\n uint256 index;\n require atIndex(e, index) == a;\n assert contains(e, a);\n}",
"type": "rule",
"id": "cover_at_contains",
"title": "contains() succeeds if atIndex() succeeded"
},
{
"content": "/**\n * @title cover properties, checking various array lengths\n * @notice The assertion should fail - it's a cover property written as an assertion. For large length, beyond loop_iter the assertion should pass.\n **/\n\nrule cover_len0(){requireInvariant addressSetInvariant();assert mirrorArrayLen() != 0;}",
"type": "rule",
"id": "cover_len0",
"title": "cover properties, checking various array lengths",
"notice": "The assertion should fail - it's a cover property written as an assertion. For large length, beyond loop_iter the assertion should pass."
},
{
"content": "rule cover_len1(){requireInvariant addressSetInvariant();assert mirrorArrayLen() != 1;}",
"type": "rule",
"id": "cover_len1",
"title": "Cover len1"
},
{
"content": "rule cover_len2(){requireInvariant addressSetInvariant();assert mirrorArrayLen() != 2;}",
"type": "rule",
"id": "cover_len2",
"title": "Cover len2"
},
{
"content": "rule cover_len3(){requireInvariant addressSetInvariant();assert mirrorArrayLen() != 3;}",
"type": "rule",
"id": "cover_len3",
"title": "Cover len3"
},
{
"content": "rule cover_len4(){requireInvariant addressSetInvariant();assert mirrorArrayLen() != 4;}",
"type": "rule",
"id": "cover_len4",
"title": "Cover len4"
},
{
"content": "rule cover_len5(){requireInvariant addressSetInvariant();assert mirrorArrayLen() != 5;}",
"type": "rule",
"id": "cover_len5",
"title": "Cover len5"
},
{
"content": "rule cover_len6(){requireInvariant addressSetInvariant();assert mirrorArrayLen() != 6;}",
"type": "rule",
"id": "cover_len6",
"title": "Cover len6"
},
{
"content": "rule cover_len7(){requireInvariant addressSetInvariant();assert mirrorArrayLen() != 7;}",
"type": "rule",
"id": "cover_len7",
"title": "Cover len7"
},
{
"content": "rule cover_len8(){requireInvariant addressSetInvariant(); assert mirrorArrayLen() != 8;}",
"type": "rule",
"id": "cover_len8",
"title": "Cover len8"
},
{
"content": "rule cover_len16(){requireInvariant addressSetInvariant(); assert mirrorArrayLen() != 16;}",
"type": "rule",
"id": "cover_len16",
"title": "Cover len16"
},
{
"content": "rule cover_len32(){requireInvariant addressSetInvariant(); assert mirrorArrayLen() != 32;}",
"type": "rule",
"id": "cover_len32",
"title": "Cover len32"
},
{
"content": "rule cover_len64(){requireInvariant addressSetInvariant(); assert mirrorArrayLen() != 64;}",
"type": "rule",
"id": "cover_len64",
"title": "Cover len64"
},
{
"content": "rule cover_len128(){requireInvariant addressSetInvariant(); assert mirrorArrayLen() != 128;}",
"type": "rule",
"id": "cover_len128",
"title": "Cover len128"
},
{
"content": "rule cover_len256(){requireInvariant addressSetInvariant(); assert mirrorArrayLen() != 256;}",
"type": "rule",
"id": "cover_len256",
"title": "Cover len256"
},
{
"content": "rule cover_len512(){requireInvariant addressSetInvariant(); assert mirrorArrayLen() != 512;}",
"type": "rule",
"id": "cover_len512",
"title": "Cover len512"
},
{
"content": "rule cover_len1024(){requireInvariant addressSetInvariant(); assert mirrorArrayLen() != 1024;}",
"type": "rule",
"id": "cover_len1024",
"title": "Cover len1024"
}
]
================================================
FILE: certora/gho/specs/set.spec
================================================
methods{
function getFacilitatorsListLen() external returns (uint256) envfree;
}
definition MAX_UINT256() returns uint256 = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
definition MAX_UINT256Bytes32() returns bytes32 = to_bytes32(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); //todo: remove once CERT-1060 is resolved
definition TWO_TO_160() returns uint256 = 0x10000000000000000000000000000000000000000;
/**
* Set map entries point to valid array entries
* @notice an essential condition of the set, should hold for evert Set implementation
* @return true if all map entries points to valid indexes of the array.
*/
definition MAP_POINTS_INSIDE_ARRAY() returns bool = forall bytes32 a. mirrorMap[a] <= mirrorArrayLen;
/**
* Set map is the inverse function of set array.
* @notice an essential condition of the set, should hold for evert Set implementation
* @notice this condition depends on the other set conditions, but the other conditions do not depend on this condition.
* If this condition is omitted the rest of the conditions still hold, but the other conditions are required to prove this condition.
* @return true if for every valid index of the array it holds that map(array(index)) == index + 1.
*/
definition MAP_IS_INVERSE_OF_ARRAY() returns bool = forall uint256 i. (i < mirrorArrayLen) => to_mathint(mirrorMap[mirrorArray[i]]) == i + 1;
/**
* Set array is the inverse function of set map
* @notice an essential condition of the set, should hold for evert Set implementation
* @return true if for every non-zero bytes32 value stored in in the set map it holds that array(map(value) - 1) == value
*/
definition ARRAY_IS_INVERSE_OF_MAP() returns bool = forall bytes32 a. forall uint256 b. to_mathint(b) == mirrorMap[a]-1 => (mirrorMap[a] != 0) => (mirrorArray[b] == a);
/**
* load array length
* @notice a dummy condition that forces load of array length, using it forces initialization of mirrorArrayLen
* @return always true
*/
definition CVL_LOAD_ARRAY_LENGTH() returns bool = (getFacilitatorsListLen() == getFacilitatorsListLen());
/**
* Set-general condition, encapsulating all conditions of Set
* @notice this condition recaps the general characteristics of Set. It should hold for all set implementations i.e. AddressSet, UintSet, Bytes32Set
* @return conjunction of the Set three essential properties.
*/
definition SET_INVARIANT() returns bool = MAP_POINTS_INSIDE_ARRAY() && MAP_IS_INVERSE_OF_ARRAY() && ARRAY_IS_INVERSE_OF_MAP() && CVL_LOAD_ARRAY_LENGTH();
/**
* Size of stored value does not exceed the size of an address type.
* @notice must be used for AddressSet, must not be used for Bytes32Set, UintSet
* @return true if all array entries are less than 160 bits.
**/
definition VALUE_IN_BOUNDS_OF_TYPE_ADDRESS() returns bool = (forall uint256 i. (mirrorArray[i]) & to_bytes32(max_uint160) == mirrorArray[i]);
/**
* A complete invariant condition for AddressSet
* @notice invariant addressSetInvariant proves that this condition holds
* @return conjunction of the Set-general and AddressSet-specific conditions
**/
definition ADDRESS_SET_INVARIANT() returns bool = SET_INVARIANT() && VALUE_IN_BOUNDS_OF_TYPE_ADDRESS();
/**
* A complete invariant condition for UintSet, Bytes32Set
* @notice for UintSet and Bytes2St no type-specific condition is required because the type size is the same as the native type (bytes32) size
* @return the Set-general condition
**/
definition UINT_SET_INVARIANT() returns bool = SET_INVARIANT();
/**
* Out of bound array entries are zero
* @notice A non-essential condition. This condition can be proven as an invariant, but it is not necessary for proving the Set correctness.
* @return true if all entries beyond array length are zero
**/
definition ARRAY_OUT_OF_BOUND_ZERO() returns bool = forall uint256 i. (i >= mirrorArrayLen) => (mirrorArray[i] == to_bytes32(0));
// For CVL use
/**
* ghost mirror map, mimics Set map
**/
ghost mapping(bytes32 => uint256) mirrorMap{
init_state axiom forall bytes32 a. mirrorMap[a] == 0;
axiom forall bytes32 a. mirrorMap[a] >= 0 && mirrorMap[a] <= MAX_UINT256(); //todo: remove once https://certora.atlassian.net/browse/CERT-1060 is resolved
}
/**
* ghost mirror array, mimics Set array
**/
ghost mapping(uint256 => bytes32) mirrorArray{
init_state axiom forall uint256 i. mirrorArray[i] == to_bytes32(0);
axiom forall uint256 a. mirrorArray[a] & MAX_UINT256Bytes32() == mirrorArray[a];
// axiom forall uint256 a. to_uint256(mirrorArray[a]) >= 0 && to_uint256(mirrorArray[a]) <= MAX_UINT256(); //todo: remove once CERT-1060 is resolved
//axiom forall uint256 a. to_mathint(mirrorArray[a]) >= 0 && to_mathint(mirrorArray[a]) <= MAX_UINT256(); //todo: use this axiom when cast bytes32 to mathint is supported
}
/**
* ghost mirror array length, mimics Set array length
* @notice ghost includes an assumption about the array length.
* If the assumption were not written in the ghost function it should be written in every rule and invariant.
* The assumption holds: breaking the assumptions would violate the invariant condition 'map(array(index)) == index + 1'. Set map uses 0 as a sentinel value, so the array cannot contain MAX_INT different values.
* The assumption is necessary: if a value is added when length==MAX_INT then length overflows and becomes zero.
**/
ghost uint256 mirrorArrayLen{
init_state axiom mirrorArrayLen == 0;
axiom to_mathint(mirrorArrayLen) < TWO_TO_160() - 1; //todo: remove once CERT-1060 is resolved
}
/**
* hook for Set array stores
* @dev user of this spec must replace _list with the instance name of the Set.
**/
hook Sstore _facilitatorsList .(offset 0)[INDEX uint256 index] bytes32 newValue (bytes32 oldValue) STORAGE {
mirrorArray[index] = newValue;
}
/**
* hook for Set array loads
* @dev user of this spec must replace _list with the instance name of the Set.
**/
hook Sload bytes32 value _facilitatorsList .(offset 0)[INDEX uint256 index] STORAGE {
require(mirrorArray[index] == value);
}
/**
* hook for Set map stores
* @dev user of this spec must replace _list with the instance name of the Set.
**/
hook Sstore _facilitatorsList .(offset 32)[KEY bytes32 key] uint256 newIndex (uint256 oldIndex) STORAGE {
mirrorMap[key] = newIndex;
}
/**
* hook for Set map loads
* @dev user of this spec must replace _list with the instance name of the Set.
**/
hook Sload uint256 index _facilitatorsList .(offset 32)[KEY bytes32 key] STORAGE {
require(mirrorMap[key] == index);
}
/**
* hook for Set array length stores
* @dev user of this spec must replace _list with the instance name of the Set.
**/
hook Sstore _facilitatorsList .(offset 0).(offset 0) uint256 newLen (uint256 oldLen) STORAGE {
mirrorArrayLen = newLen;
}
/**
* hook for Set array length load
* @dev user of this spec must replace _facilitatorsList with the instance name of the Set.
**/
hook Sload uint256 len _facilitatorsList .(offset 0).(offset 0) STORAGE {
require mirrorArrayLen == len;
}
/**
* main Set general invariant
**/
invariant setInvariant()
SET_INVARIANT();
/**
* main AddressSet invariant
* @dev user of the spec should add 'requireInvariant addressSetInvariant();' to every rule and invariant that refer to a contract that instantiates AddressSet
**/
invariant addressSetInvariant()
ADDRESS_SET_INVARIANT();
/**
* addAddress() successfully adds an address
**/
rule api_add_succeeded()
{
env e;
address a;
requireInvariant addressSetInvariant();
require !contains(e, a);
assert addAddress(e, a);
assert contains(e, a);
}
/**
* addAddress() fails to add an address if it already exists
* @notice check set membership using contains()
**/
rule api_add_failed_contains()
{
env e;
address a;
requireInvariant addressSetInvariant();
require contain(e, a);
assert !addAddress(e, a);
}
/**
* addAddress() fails to add an address if it already exists
* @notice check set membership using atIndex()
**/
rule api_add_failed_at()
{
env e;
address a;
uint256 index;
requireInvariant addressSetInvariant();
require atIndex(e, index) == a;
assert !addAddress(e, a);
}
/**
* contains() succeed after addAddress succeeded
**/
rule api_address_contained_after_add()
{
env e;
address a;
requireInvariant addressSetInvariant();
addAddress(e, a);
assert contains(e, a);
}
/**
* _removeAddress() succeeds to remove an address if it existed
* @notice check set membership using contains()
**/
rule api_remove_succeeded_contains()
{
env e;
address a;
requireInvariant addressSetInvariant();
require contains(e, a);
assert _removeAddress(e, a);
}
/**
* _removeAddress() fails to remove address if it didn't exist
**/
rule api_remove_failed()
{
env e;
address a;
requireInvariant addressSetInvariant();
require !contains(e, a);
assert !_removeAddress(e, a);
}
/**
* _removeAddress() succeeds to remove an address if it existed
* @notice check set membership using atIndex()
**/
rule api_remove_succeeded_at()
{
env e;
address a;
uint256 index;
requireInvariant addressSetInvariant();
require atIndex(e, index) == a;
assert _removeAddress(e, a);
}
/**
* contains() failed after an address was removed
**/
rule api_not_contains_after_remove()
{
env e;
address a;
requireInvariant addressSetInvariant();
_removeAddress(e, a);
assert !contains(e, a);
}
/**
* contains() succeeds if atIndex() succeeded
**/
rule cover_at_contains()
{
env e;
address a = 0;
requireInvariant addressSetInvariant();
uint256 index;
require atIndex(e, index) == a;
assert contains(e, a);
}
/**
* cover properties, checking various array lengths
* @notice The assertion should fail - it's a cover property written as an assertion. For large length, beyond loop_iter the assertion should pass.
**/
rule cover_len0(){requireInvariant addressSetInvariant();assert mirrorArrayLen != 0;}
rule cover_len1(){requireInvariant addressSetInvariant();assert mirrorArrayLen != 1;}
rule cover_len2(){requireInvariant addressSetInvariant();assert mirrorArrayLen != 2;}
rule cover_len3(){requireInvariant addressSetInvariant();assert mirrorArrayLen != 3;}
rule cover_len4(){requireInvariant addressSetInvariant();assert mirrorArrayLen != 4;}
rule cover_len5(){requireInvariant addressSetInvariant();assert mirrorArrayLen != 5;}
rule cover_len6(){requireInvariant addressSetInvariant();assert mirrorArrayLen != 6;}
rule cover_len7(){requireInvariant addressSetInvariant();assert mirrorArrayLen != 7;}
rule cover_len8(){requireInvariant addressSetInvariant(); assert mirrorArrayLen != 8;}
rule cover_len16(){requireInvariant addressSetInvariant(); assert mirrorArrayLen != 16;}
rule cover_len32(){requireInvariant addressSetInvariant(); assert mirrorArrayLen != 32;}
rule cover_len64(){requireInvariant addressSetInvariant(); assert mirrorArrayLen != 64;}
rule cover_len128(){requireInvariant addressSetInvariant(); assert mirrorArrayLen != 128;}
rule cover_len256(){requireInvariant addressSetInvariant(); assert mirrorArrayLen != 256;}
rule cover_len512(){requireInvariant addressSetInvariant(); assert mirrorArrayLen != 512;}
rule cover_len1024(){requireInvariant addressSetInvariant(); assert mirrorArrayLen != 1024;}
================================================
FILE: certora/gho/specs/summarizations.spec
================================================
// Definition of RAY unit
function first_term(uint256 x, uint256 y) returns uint256 { return x; }
ghost mapping(uint256 => mapping(uint256 => uint256)) rayMulSummariztionValues;
function rayMulSummariztion(uint256 x, uint256 y) returns uint256
{
if ((x == 0) || (y == 0))
{
return 0;
}
if (x == ray())
{
return y;
}
if (y == ray())
{
return x;
}
if (y > x)
{
if (y > ray())
{
require rayMulSummariztionValues[y][x] >= x;
}
if (x > ray())
{
require rayMulSummariztionValues[y][x] >= y;
}
return rayMulSummariztionValues[y][x];
}
else{
if (x > ray())
{
require rayMulSummariztionValues[x][y] >= y;
}
if (y > ray())
{
require rayMulSummariztionValues[x][y] >= x;
}
return rayMulSummariztionValues[x][y];
}
}
================================================
FILE: certora/gsm/conf/gsm/FixedFeeStrategy.conf
================================================
{
"files": [
"certora/gsm/harness/FixedFeeStrategyHarness.sol",
],
"packages": [
"@aave/core-v3/=lib/aave-v3-core",
"@aave/periphery-v3/=lib/aave-v3-periphery",
"@aave/=lib/aave-token",
"@openzeppelin/=lib/openzeppelin-contracts",
],
"assert_autofinder_success": true,
"optimistic_loop":true,
"loop_iter":"1",
"optimistic_hashing":true,
"hashing_length_bound":"416",
"solc": "solc8.10",
"rule_sanity" : "basic",
"msg": "Otakar: FixedFeeStrategy",
"multi_assert_check": true,
"smt_timeout": "4000",
"prover_args": [
"-depth 20",
],
"verify":
"FixedFeeStrategyHarness:certora/gsm/specs/gsm/FixedFeeStrategy.spec",
}
================================================
FILE: certora/gsm/conf/gsm/OracleSwapFreezer.conf
================================================
{
"files": [
"certora/gsm/harness/OracleSwapFreezerHarness.sol",
"src/contracts/facilitators/gsm/swapFreezer/OracleSwapFreezer.sol",
],
"packages": [
"@aave/core-v3/=lib/aave-v3-core",
"@aave/periphery-v3/=lib/aave-v3-periphery",
"@aave/=lib/aave-token",
"@openzeppelin/=lib/openzeppelin-contracts",
],
"assert_autofinder_success": true,
"optimistic_loop":true,
"loop_iter":"1",
"optimistic_hashing":true,
"hashing_length_bound":"416",
"solc": "solc8.10",
"rule_sanity" : "basic",
"multi_assert_check": true,
"msg": "OracleSwapFreezer",
"smt_timeout": "4000",
"prover_args": [
"-copyLoopUnroll 6",
"-depth 20",
],
"verify":
"OracleSwapFreezerHarness:certora/gsm/specs/gsm/OracleSwapFreezer.spec",
}
================================================
FILE: certora/gsm/conf/gsm/balances-buy.conf
================================================
{
"files": [
"certora/gsm/harness/GsmHarness.sol",
"certora/gsm/harness/DummyERC20A.sol",
"certora/gsm/harness/DummyERC20B.sol",
"certora/gsm/harness/FixedPriceStrategyHarness.sol",
"certora/gsm/harness/FixedFeeStrategyHarness.sol",
"certora/gsm/harness/DiffHelper.sol",
"src/contracts/gho/GhoToken.sol",
],
"link": [
"GsmHarness:GHO_TOKEN=GhoToken",
"GsmHarness:PRICE_STRATEGY=FixedPriceStrategyHarness",
"GsmHarness:_feeStrategy=FixedFeeStrategyHarness",
],
"packages": [
"@aave/core-v3/=lib/aave-v3-core",
"@aave/periphery-v3/=lib/aave-v3-periphery",
"@aave/=lib/aave-token",
"@openzeppelin/=lib/openzeppelin-contracts",
],
"assert_autofinder_success": true,
"optimistic_loop":true,
"loop_iter":"1",
"rule_sanity" : "basic",
"optimistic_hashing":true,
"hashing_length_bound":"416",
"solc": "solc8.10",
"msg": "balances - buy",
"prover_args": [
"-copyLoopUnroll 6",
"-depth 20",
],
"verify":
"GsmHarness:certora/gsm/specs/gsm/balances-buy.spec",
}
================================================
FILE: certora/gsm/conf/gsm/balances-sell.conf
================================================
{
"files": [
"certora/gsm/harness/GsmHarness.sol",
"certora/gsm/harness/DummyERC20A.sol",
"certora/gsm/harness/DummyERC20B.sol",
"certora/gsm/harness/FixedPriceStrategyHarness.sol",
"certora/gsm/harness/FixedFeeStrategyHarness.sol",
"certora/gsm/harness/DiffHelper.sol",
"src/contracts/gho/GhoToken.sol",
],
"link": [
"GsmHarness:GHO_TOKEN=GhoToken",
"GsmHarness:PRICE_STRATEGY=FixedPriceStrategyHarness",
"GsmHarness:_feeStrategy=FixedFeeStrategyHarness",
],
"packages": [
"@aave/core-v3/=lib/aave-v3-core",
"@aave/periphery-v3/=lib/aave-v3-periphery",
"@aave/=lib/aave-token",
"@openzeppelin/=lib/openzeppelin-contracts",
],
"assert_autofinder_success": true,
"optimistic_loop":true,
"loop_iter":"1",
"rule_sanity" : "basic",
"optimistic_hashing":true,
"hashing_length_bound":"416",
"solc": "solc8.10",
"msg": "balances - sell",
"prover_args": [
"-copyLoopUnroll 6",
"-depth 20",
],
"verify":
"GsmHarness:certora/gsm/specs/gsm/balances-sell.spec",
}
================================================
FILE: certora/gsm/conf/gsm/fees-buy.conf
================================================
{
"files": [
"certora/gsm/harness/GsmHarness.sol",
"certora/gsm/harness/DummyERC20A.sol",
"certora/gsm/harness/DummyERC20B.sol",
"certora/gsm/harness/FixedPriceStrategyHarness.sol",
"certora/gsm/harness/FixedFeeStrategyHarness.sol",
"certora/gsm/harness/DiffHelper.sol",
"src/contracts/gho/GhoToken.sol",
],
"link": [
"GsmHarness:GHO_TOKEN=GhoToken",
"GsmHarness:PRICE_STRATEGY=FixedPriceStrategyHarness",
"GsmHarness:_feeStrategy=FixedFeeStrategyHarness",
],
"packages": [
"@aave/core-v3/=lib/aave-v3-core",
"@aave/periphery-v3/=lib/aave-v3-periphery",
"@aave/=lib/aave-token",
"@openzeppelin/=lib/openzeppelin-contracts",
],
"assert_autofinder_success": true,
"optimistic_loop":true,
"loop_iter":"1",
"rule_sanity" : "basic",
"optimistic_hashing":true,
"hashing_length_bound":"416",
"solc": "solc8.10",
"msg": "fees - buy depth 20 mult-assert noadaptive no linears",
"multi_assert_check": true,
"prover_args": [
"-copyLoopUnroll 6",
"-depth 20",
"-adaptiveSolverConfig false",
"-smt_nonLinearArithmetic true",
],
"verify":
"GsmHarness:certora/gsm/specs/gsm/fees-buy.spec",
}
================================================
FILE: certora/gsm/conf/gsm/fees-sell.conf
================================================
{
"files": [
"certora/gsm/harness/GsmHarness.sol",
"certora/gsm/harness/DummyERC20A.sol",
"certora/gsm/harness/DummyERC20B.sol",
"certora/gsm/harness/FixedPriceStrategyHarness.sol",
"certora/gsm/harness/FixedFeeStrategyHarness.sol",
"certora/gsm/harness/DiffHelper.sol",
"src/contracts/gho/GhoToken.sol",
],
"link": [
"GsmHarness:GHO_TOKEN=GhoToken",
"GsmHarness:PRICE_STRATEGY=FixedPriceStrategyHarness",
"GsmHarness:_feeStrategy=FixedFeeStrategyHarness",
],
"packages": [
"@aave/core-v3/=lib/aave-v3-core",
"@aave/periphery-v3/=lib/aave-v3-periphery",
"@aave/=lib/aave-token",
"@openzeppelin/=lib/openzeppelin-contracts",
],
"assert_autofinder_success": true,
"optimistic_loop":true,
"loop_iter":"1",
"rule_sanity" : "basic",
"optimistic_hashing":true,
"hashing_length_bound":"416",
"solc": "solc8.10",
"smt_timeout": "7200",
"msg": "fees - sell",
"multi_assert_check": true,
"prover_args": [
"-copyLoopUnroll 6",
"-depth 20",
"-adaptiveSolverConfig false",
"-smt_nonLinearArithmetic true",
],
"verify":
"GsmHarness:certora/gsm/specs/gsm/fees-sell.spec"
}
================================================
FILE: certora/gsm/conf/gsm/finishedRules.conf
================================================
{
"files": [
"certora/gsm/harness/GsmHarness.sol",
"certora/gsm/harness/DummyERC20A.sol",
"certora/gsm/harness/DummyERC20B.sol",
"certora/gsm/harness/ERC20Helper.sol",
"certora/gsm/harness/FixedPriceStrategyHarness.sol",
"certora/gsm/harness/FixedFeeStrategyHarness.sol",
"src/contracts/gho/GhoToken.sol",
],
"link": [
"GsmHarness:GHO_TOKEN=GhoToken",
"GsmHarness:PRICE_STRATEGY=FixedPriceStrategyHarness",
"GsmHarness:_feeStrategy=FixedFeeStrategyHarness",
],
"packages": [
"@aave/core-v3/=lib/aave-v3-core",
"@aave/periphery-v3/=lib/aave-v3-periphery",
"@aave/=lib/aave-token",
"@openzeppelin/=lib/openzeppelin-contracts",
],
"assert_autofinder_success": true,
"optimistic_loop":true,
"loop_iter":"1",
"optimistic_hashing":true,
"hashing_length_bound":"416",
"rule_sanity" : "basic",
"solc": "solc8.10",
"msg": "optimalityOfBuy",
"smt_timeout": "4000",
"multi_assert_check": true,
"prover_args": [
"-copyLoopUnroll 6",
"-depth 20",
],
"verify":
"GsmHarness:certora/gsm/specs/gsm/gho-gsm-finishedRules.spec",
}
================================================
FILE: certora/gsm/conf/gsm/getAmount_properties.conf
================================================
{
"files": [
"certora/gsm/harness/GsmHarness.sol",
"certora/gsm/harness/DummyERC20A.sol",
"certora/gsm/harness/DummyERC20B.sol",
"certora/gsm/harness/ERC20Helper.sol",
"certora/gsm/harness/FixedPriceStrategyHarness.sol",
"certora/gsm/harness/FixedFeeStrategyHarness.sol",
"src/contracts/gho/GhoToken.sol",
],
"link": [
"GsmHarness:GHO_TOKEN=GhoToken",
"GsmHarness:PRICE_STRATEGY=FixedPriceStrategyHarness",
"GsmHarness:_feeStrategy=FixedFeeStrategyHarness",
],
"packages": [
"@aave/core-v3/=lib/aave-v3-core",
"@aave/periphery-v3/=lib/aave-v3-periphery",
"@aave/=lib/aave-token",
"@openzeppelin/=lib/openzeppelin-contracts",
],
"assert_autofinder_success": true,
"optimistic_loop":true,
"loop_iter":"1",
"optimistic_hashing":true,
"rule_sanity" : "basic",
"hashing_length_bound":"416",
"solc": "solc8.10",
"smt_timeout": "7200",
"multi_assert_check": true,
"msg": "gsm properties",
"prover_args": [
"-copyLoopUnroll 6",
"-depth 20"
],
"verify": "GsmHarness:certora/gsm/specs/gsm/getAmount_properties.spec",
}
================================================
FILE: certora/gsm/conf/gsm/gho-assetToGhoInvertibility.conf
================================================
{
"files": [
"certora/gsm/harness/GsmHarness.sol",
"certora/gsm/harness/DummyERC20A.sol",
"certora/gsm/harness/DummyERC20B.sol",
"certora/gsm/harness/ERC20Helper.sol",
"certora/gsm/harness/FixedPriceStrategyHarness.sol",
"certora/gsm/harness/FixedFeeStrategyHarness.sol",
"src/contracts/gho/GhoToken.sol",
],
"link": [
"GsmHarness:GHO_TOKEN=GhoToken",
"GsmHarness:PRICE_STRATEGY=FixedPriceStrategyHarness",
"GsmHarness:_feeStrategy=FixedFeeStrategyHarness"
],
"packages": [
"@aave/core-v3/=lib/aave-v3-core",
"@aave/periphery-v3/=lib/aave-v3-periphery",
"@aave/=lib/aave-token",
"@openzeppelin/=lib/openzeppelin-contracts",
],
"assert_autofinder_success": true,
"optimistic_loop":true,
"loop_iter":"1",
"optimistic_hashing":true,
"rule_sanity" : "basic",
"hashing_length_bound":"416",
"solc": "solc8.10",
"msg": "gsm getAsset/GhoAmountForBuy/SellAsset invertibility rules",
"smt_timeout": "7200",
"prover_args": [
"-copyLoopUnroll 6",
"-depth 20"
],
"multi_assert_check": true,
"verify":
"GsmHarness:certora/gsm/specs/gsm/AssetToGhoInvertibility.spec",
}
================================================
FILE: certora/gsm/conf/gsm/gho-fixedPriceStrategy.conf
================================================
{
"files": [
"certora/gsm/harness/FixedPriceStrategyHarness.sol",
],
"packages": [
"@aave/core-v3/=lib/aave-v3-core",
"@aave/periphery-v3/=lib/aave-v3-periphery",
"@aave/=lib/aave-token",
"@openzeppelin/=lib/openzeppelin-contracts",
],
"assert_autofinder_success": true,
"optimistic_loop":true,
"loop_iter":"1",
"optimistic_hashing":true,
"hashing_length_bound":"416",
"solc": "solc8.10",
"msg": "gsm4626 - getAssetAmountInGho and getGhoAmountInAsset are inverse",
"smt_timeout": "7200",
"rule_sanity" : "basic",
"prover_args": [
"-copyLoopUnroll 6",
"-depth 20"
],
"multi_assert_check": true,
"verify":
"FixedPriceStrategyHarness:certora/gsm/specs/gsm/FixedPriceStrategy.spec",
}
================================================
FILE: certora/gsm/conf/gsm/gho-gsm-2.conf
================================================
{
"files": [
"certora/gsm/harness/GsmHarness.sol",
"certora/gsm/harness/DummyERC20A.sol",
"certora/gsm/harness/DummyERC20B.sol",
"certora/gsm/harness/FixedPriceStrategyHarness.sol",
"certora/gsm/harness/FixedFeeStrategyHarness.sol",
"certora/gsm/harness/ERC20Helper.sol:ERC20Helper",
"src/contracts/gho/GhoToken.sol",
],
"link": [
"GsmHarness:GHO_TOKEN=GhoToken",
"GsmHarness:PRICE_STRATEGY=FixedPriceStrategyHarness",
"GsmHarness:_feeStrategy=FixedFeeStrategyHarness",
"GsmHarness:UNDERLYING_ASSET=DummyERC20B"
],
"packages": [
"@aave/core-v3/=lib/aave-v3-core",
"@aave/periphery-v3/=lib/aave-v3-periphery",
"@aave/=lib/aave-token",
"@openzeppelin/=lib/openzeppelin-contracts",
],
"parametric_contracts": [ "GsmHarness"],
"assert_autofinder_success": true,
"optimistic_loop":true,
"loop_iter":"1",
"rule_sanity" : "basic",
"optimistic_hashing":true,
"hashing_length_bound":"416",
"solc": "solc8.10",
"msg": "gsm properties",
"prover_args": [
"-copyLoopUnroll 6",
"-depth 20",
"-smt_hashingScheme plainInjectivity"
],
"verify":
"GsmHarness:certora/gsm/specs/gsm/gho-gsm-2.spec",
}
================================================
FILE: certora/gsm/conf/gsm/gho-gsm.conf
================================================
{
"files": [
"certora/gsm/harness/GsmHarness.sol",
"certora/gsm/harness/DummyERC20A.sol",
"certora/gsm/harness/DummyERC20B.sol",
"certora/gsm/harness/ERC20Helper.sol",
"certora/gsm/harness/FixedPriceStrategyHarness.sol",
"certora/gsm/harness/FixedFeeStrategyHarness.sol",
"src/contracts/gho/GhoToken.sol",
],
"link": [
"GsmHarness:GHO_TOKEN=GhoToken",
"GsmHarness:PRICE_STRATEGY=FixedPriceStrategyHarness",
"GsmHarness:_feeStrategy=FixedFeeStrategyHarness",
],
"packages": [
"@aave/core-v3/=lib/aave-v3-core",
"@aave/periphery-v3/=lib/aave-v3-periphery",
"@aave/=lib/aave-token",
"@openzeppelin/=lib/openzeppelin-contracts",
],
"parametric_contracts": [ "GsmHarness"],
"assert_autofinder_success": true,
"optimistic_loop":true,
"loop_iter":"1",
"optimistic_hashing":true,
"rule_sanity" : "basic",
"hashing_length_bound":"416",
"solc": "solc8.10",
"msg": "gsm properties",
"smt_timeout": "7200",
"prover_args": [
"-copyLoopUnroll 6",
"-depth 20"
],
"verify":
"GsmHarness:certora/gsm/specs/gsm/gho-gsm.spec",
}
================================================
FILE: certora/gsm/conf/gsm/gho-gsm_inverse.conf
================================================
{
"files": [
"certora/gsm/harness/GsmHarness.sol",
"certora/gsm/harness/DummyERC20A.sol",
"certora/gsm/harness/DummyERC20B.sol",
"certora/gsm/harness/FixedPriceStrategyHarness.sol",
"certora/gsm/harness/FixedFeeStrategyHarness.sol",
"certora/gsm/harness/ERC20Helper.sol",
"src/contracts/gho/GhoToken.sol",
],
"link": [
"GsmHarness:GHO_TOKEN=GhoToken",
"GsmHarness:PRICE_STRATEGY=FixedPriceStrategyHarness",
"GsmHarness:_feeStrategy=FixedFeeStrategyHarness",
],
"packages": [
"@aave/core-v3/=lib/aave-v3-core",
"@aave/periphery-v3/=lib/aave-v3-periphery",
"@aave/=lib/aave-token",
"@openzeppelin/=lib/openzeppelin-contracts",
],
"assert_autofinder_success": true,
"optimistic_loop":true,
"loop_iter":"1",
"optimistic_hashing":true,
"hashing_length_bound":"416",
"solc": "solc8.10",
"rule_sanity" : "basic",
"msg": "gsm properties",
"smt_timeout": "7200",
"prover_args": [
"-copyLoopUnroll 6",
"-depth 20"
],
"verify":
"GsmHarness:certora/gsm/specs/gsm/gho-gsm_inverse.spec",
}
================================================
FILE: certora/gsm/conf/gsm/optimality.conf
================================================
{
"files": [
"certora/gsm/harness/GsmHarness.sol",
"certora/gsm/harness/DummyERC20A.sol",
"certora/gsm/harness/DummyERC20B.sol",
"certora/gsm/harness/ERC20Helper.sol",
"certora/gsm/harness/FixedPriceStrategyHarness.sol",
"certora/gsm/harness/FixedFeeStrategyHarness.sol",
"src/contracts/gho/GhoToken.sol",
],
"link": [
"GsmHarness:GHO_TOKEN=GhoToken",
"GsmHarness:PRICE_STRATEGY=FixedPriceStrategyHarness",
"GsmHarness:_feeStrategy=FixedFeeStrategyHarness",
],
"packages": [
"@aave/core-v3/=lib/aave-v3-core",
"@aave/periphery-v3/=lib/aave-v3-periphery",
"@aave/=lib/aave-token",
"@openzeppelin/=lib/openzeppelin-contracts",
],
"assert_autofinder_success": true,
"optimistic_loop":true,
"rule_sanity" : "basic",
"loop_iter":"1",
"optimistic_hashing":true,
"hashing_length_bound":"416",
"solc": "solc8.10",
"msg": "optimality of buyAsset - multi_assert",
"multi_assert_check": true,
"prover_args": [
"-copyLoopUnroll 6",
"-depth 20"
],
"verify":
"GsmHarness:certora/gsm/specs/gsm/optimality.spec",
}
================================================
FILE: certora/gsm/conf/gsm4626/balances-buy-4626.conf
================================================
{
"files": [
"certora/gsm/harness/Gsm4626Harness.sol",
"certora/gsm/harness/DummyERC20A.sol",
"certora/gsm/harness/DummyERC20B.sol",
"certora/gsm/harness/ERC20Helper.sol",
"certora/gsm/harness/FixedPriceStrate
gitextract_qacr2h46/ ├── .github/ │ ├── CODEOWNERS │ └── workflows/ │ ├── certora-gho-505.yml │ ├── certora-gho.yml │ ├── certora-gsm-4626.yml │ ├── certora-gsm.yml │ ├── certora-steward.yml │ ├── dependency-review.yml │ ├── node.js.yml │ └── sync-issue.yml ├── .gitignore ├── .gitmodules ├── .husky/ │ ├── .gitignore │ └── pre-commit ├── .prettierignore ├── .prettierrc ├── .solcover.js ├── .vscode/ │ └── settings.json ├── LICENSE ├── README.md ├── certora/ │ ├── gho/ │ │ ├── Makefile │ │ ├── applyHarness.patch │ │ ├── conf/ │ │ │ ├── verifyFlashMinter.conf │ │ │ ├── verifyGhoAToken.conf │ │ │ ├── verifyGhoDiscountRateStrategy.conf │ │ │ ├── verifyGhoToken.conf │ │ │ ├── verifyGhoVariableDebtToken-rayMulDiv-summarization.conf │ │ │ ├── verifyGhoVariableDebtToken.conf │ │ │ ├── verifyGhoVariableDebtTokenInternal.conf │ │ │ ├── verifyGhoVariableDebtToken_specialBranch.conf │ │ │ ├── verifyGhoVariableDebtToken_summarized.conf │ │ │ └── verifyUpgradeableGhoToken.conf │ │ ├── harness/ │ │ │ ├── DummyERC20A.sol │ │ │ ├── DummyERC20B.sol │ │ │ ├── DummyERC20Impl.sol │ │ │ ├── DummyERC20WithTimedBalanceOf.sol │ │ │ ├── DummyPool.sol │ │ │ ├── GhoDiscountRateStrategyHarness.sol │ │ │ ├── GhoTokenHarness.sol │ │ │ ├── MockFlashBorrower.sol │ │ │ ├── UpgradeableGhoTokenHarness.sol │ │ │ ├── ghoVariableDebtTokenHarness.sol │ │ │ └── ghoVariableDebtTokenHarnessInternal.sol │ │ ├── munged/ │ │ │ └── .gitignore │ │ └── specs/ │ │ ├── VariableDebtToken.spec │ │ ├── erc20.spec │ │ ├── flashMinter.spec │ │ ├── ghoAToken.spec │ │ ├── ghoDiscountRateStrategy.spec │ │ ├── ghoToken.spec │ │ ├── ghoVariableDebtToken-rayMulDiv-summarization.spec │ │ ├── ghoVariableDebtToken.spec │ │ ├── ghoVariableDebtTokenInternal.spec │ │ ├── ghoVariableDebtToken_summarized.spec │ │ ├── set-natspec.json │ │ ├── set.spec │ │ └── summarizations.spec │ ├── gsm/ │ │ ├── conf/ │ │ │ ├── gsm/ │ │ │ │ ├── FixedFeeStrategy.conf │ │ │ │ ├── OracleSwapFreezer.conf │ │ │ │ ├── balances-buy.conf │ │ │ │ ├── balances-sell.conf │ │ │ │ ├── fees-buy.conf │ │ │ │ ├── fees-sell.conf │ │ │ │ ├── finishedRules.conf │ │ │ │ ├── getAmount_properties.conf │ │ │ │ ├── gho-assetToGhoInvertibility.conf │ │ │ │ ├── gho-fixedPriceStrategy.conf │ │ │ │ ├── gho-gsm-2.conf │ │ │ │ ├── gho-gsm.conf │ │ │ │ ├── gho-gsm_inverse.conf │ │ │ │ └── optimality.conf │ │ │ └── gsm4626/ │ │ │ ├── balances-buy-4626.conf │ │ │ ├── balances-sell-4626.conf │ │ │ ├── fees-buy-4626.conf │ │ │ ├── fees-sell-4626.conf │ │ │ ├── finishedRules4626.conf │ │ │ ├── getAmount_4626_properties.conf │ │ │ ├── gho-assetToGhoInvertibility4626.conf │ │ │ ├── gho-fixedPriceStrategy4626.conf │ │ │ ├── gho-gsm4626-2.conf │ │ │ ├── gho-gsm4626.conf │ │ │ ├── gho-gsm_4626_inverse.conf │ │ │ └── optimality4626.conf │ │ ├── harness/ │ │ │ ├── DiffHelper.sol │ │ │ ├── DummyERC20A.sol │ │ │ ├── DummyERC20B.sol │ │ │ ├── DummyERC20Impl.sol │ │ │ ├── ERC20Helper.sol │ │ │ ├── FixedFeeStrategyHarness.sol │ │ │ ├── FixedPriceStrategy4626Harness.sol │ │ │ ├── FixedPriceStrategyHarness.sol │ │ │ ├── Gsm4626Harness.sol │ │ │ ├── GsmHarness.sol │ │ │ └── OracleSwapFreezerHarness.sol │ │ ├── munged/ │ │ │ └── .gitignore │ │ └── specs/ │ │ ├── GsmMethods/ │ │ │ ├── aave_fee_limits.spec │ │ │ ├── aave_price_fee_limits.spec │ │ │ ├── aave_price_fee_limits_strict.spec │ │ │ ├── aave_price_limits.spec │ │ │ ├── erc20.spec │ │ │ ├── erc4626.spec │ │ │ ├── methods4626_base.spec │ │ │ ├── methods_base-Martin.spec │ │ │ ├── methods_base.spec │ │ │ ├── methods_divint_summary.spec │ │ │ └── shared.spec │ │ ├── gsm/ │ │ │ ├── AssetToGhoInvertibility.spec │ │ │ ├── FixedFeeStrategy.spec │ │ │ ├── FixedPriceStrategy.spec │ │ │ ├── OracleSwapFreezer.spec │ │ │ ├── balances-buy.spec │ │ │ ├── balances-sell.spec │ │ │ ├── fees-buy.spec │ │ │ ├── fees-sell.spec │ │ │ ├── getAmount_properties.spec │ │ │ ├── gho-gsm-2.spec │ │ │ ├── gho-gsm-Buy.spec │ │ │ ├── gho-gsm-finishedRules.spec │ │ │ ├── gho-gsm.spec │ │ │ ├── gho-gsm_inverse.spec │ │ │ └── optimality.spec │ │ └── gsm4626/ │ │ ├── AssetToGhoInvertibility4626.spec │ │ ├── FixedPriceStrategy4626.spec │ │ ├── balances-buy-4626.spec │ │ ├── balances-sell-4626.spec │ │ ├── fees-buy-4626.spec │ │ ├── fees-sell-4626.spec │ │ ├── getAmount_4626_properties.spec │ │ ├── gho-gsm-finishedRules4626.spec │ │ ├── gho-gsm4626-2.spec │ │ ├── gho-gsm4626.spec │ │ ├── gho-gsm_4626_inverse.spec │ │ └── optimality4626.spec │ └── steward/ │ ├── Makefile │ ├── applyHarness.patch │ ├── conf/ │ │ ├── GhoAaveSteward.conf │ │ ├── GhoBucketSteward.conf │ │ ├── GhoCcipSteward.conf │ │ └── GhoGsmSteward.conf │ ├── harness/ │ │ ├── GhoAaveSteward_Harness.sol │ │ ├── GhoCcipSteward_Harness.sol │ │ ├── GhoGsmSteward_Harness.sol │ │ └── GhoStewardV2_Harness.sol │ ├── munged/ │ │ └── .gitignore │ └── specs/ │ ├── GhoAaveSteward.spec │ ├── GhoBucketSteward.spec │ ├── GhoCcipSteward.spec │ └── GhoGsmSteward.spec ├── deploy/ │ ├── 00_deploy_gho_token.ts │ ├── 01_deploy_gho_oracle.ts │ ├── 02_deploy_gho_atoken.ts │ ├── 03_deploy_gho_stable_debt.ts │ ├── 04_deploy_gho_variable_debt.ts │ ├── 05_deploy_gho_interest_rate.ts │ ├── 06_deploy_gho_discount_rate.ts │ ├── 07_deploy_stakedAave_upgrade.ts │ ├── 08_deploy_gho_flashminter.ts │ └── 09_deploy_uighodataprovider.ts ├── docs/ │ └── gho-stewards.md ├── foundry.toml ├── hardhat.config.ts ├── helpers/ │ ├── config.ts │ ├── constants.ts │ ├── contract-getters.ts │ ├── hardhat-config.ts │ ├── misc-utils.ts │ └── types.ts ├── package.json ├── remappings.txt ├── setup-test-env.sh ├── slither.config.json ├── src/ │ ├── contracts/ │ │ ├── facilitators/ │ │ │ ├── aave/ │ │ │ │ ├── interestStrategy/ │ │ │ │ │ ├── FixedRateStrategyFactory.sol │ │ │ │ │ ├── GhoDiscountRateStrategy.sol │ │ │ │ │ ├── GhoInterestRateStrategy.sol │ │ │ │ │ ├── ZeroDiscountRateStrategy.sol │ │ │ │ │ └── interfaces/ │ │ │ │ │ ├── IFixedRateStrategyFactory.sol │ │ │ │ │ └── IGhoDiscountRateStrategy.sol │ │ │ │ ├── misc/ │ │ │ │ │ ├── UiGhoDataProvider.sol │ │ │ │ │ └── interfaces/ │ │ │ │ │ └── IUiGhoDataProvider.sol │ │ │ │ ├── oracle/ │ │ │ │ │ └── GhoOracle.sol │ │ │ │ └── tokens/ │ │ │ │ ├── GhoAToken.sol │ │ │ │ ├── GhoStableDebtToken.sol │ │ │ │ ├── GhoVariableDebtToken.sol │ │ │ │ ├── base/ │ │ │ │ │ └── ScaledBalanceTokenBase.sol │ │ │ │ └── interfaces/ │ │ │ │ ├── IGhoAToken.sol │ │ │ │ └── IGhoVariableDebtToken.sol │ │ │ ├── flashMinter/ │ │ │ │ ├── GhoFlashMinter.sol │ │ │ │ └── interfaces/ │ │ │ │ └── IGhoFlashMinter.sol │ │ │ └── gsm/ │ │ │ ├── Gsm.sol │ │ │ ├── Gsm4626.sol │ │ │ ├── dependencies/ │ │ │ │ └── chainlink/ │ │ │ │ └── AutomationCompatibleInterface.sol │ │ │ ├── feeStrategy/ │ │ │ │ ├── FixedFeeStrategy.sol │ │ │ │ ├── FixedFeeStrategyFactory.sol │ │ │ │ └── interfaces/ │ │ │ │ ├── IFixedFeeStrategyFactory.sol │ │ │ │ └── IGsmFeeStrategy.sol │ │ │ ├── interfaces/ │ │ │ │ ├── IGsm.sol │ │ │ │ └── IGsm4626.sol │ │ │ ├── misc/ │ │ │ │ ├── GsmRegistry.sol │ │ │ │ ├── IGsmRegistry.sol │ │ │ │ ├── SampleLiquidator.sol │ │ │ │ └── SampleSwapFreezer.sol │ │ │ ├── priceStrategy/ │ │ │ │ ├── FixedPriceStrategy.sol │ │ │ │ ├── FixedPriceStrategy4626.sol │ │ │ │ └── interfaces/ │ │ │ │ └── IGsmPriceStrategy.sol │ │ │ └── swapFreezer/ │ │ │ └── OracleSwapFreezer.sol │ │ ├── gho/ │ │ │ ├── ERC20.sol │ │ │ ├── GhoToken.sol │ │ │ ├── UpgradeableERC20.sol │ │ │ ├── UpgradeableGhoToken.sol │ │ │ └── interfaces/ │ │ │ ├── IGhoFacilitator.sol │ │ │ └── IGhoToken.sol │ │ └── misc/ │ │ ├── GhoAaveSteward.sol │ │ ├── GhoBucketSteward.sol │ │ ├── GhoCcipSteward.sol │ │ ├── GhoGsmSteward.sol │ │ ├── RiskCouncilControlled.sol │ │ ├── dependencies/ │ │ │ ├── AaveV3-1.sol │ │ │ └── Ccip.sol │ │ └── interfaces/ │ │ ├── IGhoAaveSteward.sol │ │ ├── IGhoBucketSteward.sol │ │ ├── IGhoCcipSteward.sol │ │ └── IGhoGsmSteward.sol │ ├── script/ │ │ ├── DeployGsmLaunch.s.sol │ │ └── ExternalDependencyCompiler.s.sol │ └── test/ │ ├── TestFixedRateStrategyFactory.t.sol │ ├── TestGhoAToken.t.sol │ ├── TestGhoAaveSteward.t.sol │ ├── TestGhoBase.t.sol │ ├── TestGhoBucketSteward.t.sol │ ├── TestGhoCcipSteward.t.sol │ ├── TestGhoDiscountRateStrategy.t.sol │ ├── TestGhoFlashMinter.t.sol │ ├── TestGhoGsmSteward.t.sol │ ├── TestGhoInterestRateStrategy.t.sol │ ├── TestGhoOracle.t.sol │ ├── TestGhoStableDebtToken.t.sol │ ├── TestGhoStewardsForkEthereum.t.sol │ ├── TestGhoStewardsForkRemote.t.sol │ ├── TestGhoToken.t.sol │ ├── TestGhoVariableDebtToken.t.sol │ ├── TestGhoVariableDebtTokenForked.t.sol │ ├── TestGsm.t.sol │ ├── TestGsm4626.t.sol │ ├── TestGsm4626Edge.t.sol │ ├── TestGsmFixedFeeStrategy.sol │ ├── TestGsmFixedPriceStrategy.t.sol │ ├── TestGsmFixedPriceStrategy4626.t.sol │ ├── TestGsmOracleSwapFreezer.t.sol │ ├── TestGsmRegistry.t.sol │ ├── TestGsmSampleLiquidator.t.sol │ ├── TestGsmSampleSwapFreezer.t.sol │ ├── TestGsmSwapEdge.t.sol │ ├── TestGsmSwapFuzz.t.sol │ ├── TestGsmUpgrade.t.sol │ ├── TestUiGhoDataProvider.t.sol │ ├── TestUpgradeableGhoToken.t.sol │ ├── TestZeroDiscountRateStrategy.t.sol │ ├── helpers/ │ │ ├── Constants.sol │ │ ├── DebtUtils.sol │ │ ├── ErrorsLib.sol │ │ └── Events.sol │ └── mocks/ │ ├── MockAclManager.sol │ ├── MockAddressesProvider.sol │ ├── MockConfigurator.sol │ ├── MockERC4626.sol │ ├── MockFlashBorrower.sol │ ├── MockGsmV2.sol │ ├── MockPool.sol │ ├── MockPoolDataProvider.sol │ ├── MockUpgradeable.sol │ ├── MockUpgradeableBurnMintTokenPool.sol │ └── MockUpgradeableLockReleaseTokenPool.sol ├── tasks/ │ ├── main/ │ │ ├── deploy-and-setup.ts │ │ └── gho-testnet-setup.ts │ ├── misc/ │ │ ├── network-check.ts │ │ └── print-all-deployments.ts │ ├── roles/ │ │ └── 00_gho-transfer-ownership.ts │ └── testnet-setup/ │ ├── 00_initialize-gho-reserve.ts │ ├── 01_enable-gho-borrowing.ts │ ├── 02_set-gho-oracle.ts │ ├── 03_add-gho-as-entity.ts │ ├── 04_add-gho-flashminter-as-entity.ts │ ├── 05_set-gho-addresses.ts │ └── 06_upgrade-stkAave.ts ├── test/ │ ├── __setup.test.ts │ ├── basic-borrow.test.ts │ ├── borrow-onBehalf.test.ts │ ├── discount-borrow.test.ts │ ├── discount-rebalance.test.ts │ ├── flashmint.test.ts │ ├── gho-atoken.test.ts │ ├── gho-oracle.test.ts │ ├── gho-stable-debt.test.ts │ ├── gho-token-permit.test.ts │ ├── gho-token-unit.test.ts │ ├── gho-variable-debt.test.ts │ ├── helpers/ │ │ ├── constants.ts │ │ ├── helpers.ts │ │ ├── make-suite.ts │ │ ├── math/ │ │ │ ├── calculations.ts │ │ │ └── wadraymath.ts │ │ ├── tokenization-events.ts │ │ └── user-setup.ts │ ├── initial-entitiy-configuration.test.ts │ ├── initial-reserve-configuration.test.ts │ ├── stkAave-upgrade.test.ts │ └── transfer-stkAave.test.ts └── tsconfig.json
SYMBOL INDEX (41 symbols across 8 files)
FILE: helpers/constants.ts
constant PERCENTAGE_FACTOR (line 8) | const PERCENTAGE_FACTOR = '10000';
constant HALF_PERCENTAGE (line 9) | const HALF_PERCENTAGE = BigNumber.from(PERCENTAGE_FACTOR).div(2).toStrin...
constant WAD (line 10) | const WAD = BigNumber.from(10).pow(18).toString();
constant HALF_WAD (line 11) | const HALF_WAD = BigNumber.from(WAD).div(2).toString();
constant RAY (line 12) | const RAY = BigNumber.from(10).pow(27).toString();
constant HALF_RAY (line 13) | const HALF_RAY = BigNumber.from(RAY).div(2).toString();
constant WAD_RAY_RATIO (line 14) | const WAD_RAY_RATIO = parseUnits('1', 9).toString();
constant MAX_UINT_AMOUNT (line 17) | const MAX_UINT_AMOUNT =
constant MAX_BORROW_CAP (line 19) | const MAX_BORROW_CAP = '68719476735';
constant MAX_SUPPLY_CAP (line 20) | const MAX_SUPPLY_CAP = '68719476735';
constant MAX_UNBACKED_MINT_CAP (line 21) | const MAX_UNBACKED_MINT_CAP = '68719476735';
constant ONE_YEAR (line 22) | const ONE_YEAR = '31536000';
constant YEAR (line 23) | const YEAR = 31536000;
constant ZERO_ADDRESS (line 24) | const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
constant ONE_ADDRESS (line 25) | const ONE_ADDRESS = '0x0000000000000000000000000000000000000001';
constant MOCK_USD_PRICE_IN_WEI (line 29) | const MOCK_USD_PRICE_IN_WEI = '5848466240000000';
constant USD_ADDRESS (line 30) | const USD_ADDRESS = '0x10F7Fc1F91Ba351f9C629c5947AD69bD03C05b96';
constant AAVE_REFERRAL (line 31) | const AAVE_REFERRAL = '0';
constant TEST_SNAPSHOT_ID (line 33) | const TEST_SNAPSHOT_ID = '0x1';
constant HARDHAT_CHAINID (line 34) | const HARDHAT_CHAINID = 31337;
constant COVERAGE_CHAINID (line 35) | const COVERAGE_CHAINID = 1337;
constant MAX_UINT (line 37) | const MAX_UINT = BigNumber.from(MAX_UINT_AMOUNT);
FILE: helpers/hardhat-config.ts
constant MNEMONIC (line 12) | const MNEMONIC = process.env.MNEMONIC || '';
constant MNEMONIC_PATH (line 13) | const MNEMONIC_PATH = "m/44'/60'/0'/0";
constant NETWORKS_RPC_URL (line 15) | const NETWORKS_RPC_URL: Record<string, string> = {
constant GAS_PRICE_PER_NET (line 26) | const GAS_PRICE_PER_NET: Record<string, number> = {};
constant LIVE_NETWORKS (line 28) | const LIVE_NETWORKS: Record<string, boolean> = {
function getRemappings (line 83) | function getRemappings() {
FILE: helpers/misc-utils.ts
constant FULL_DEPLOY (line 81) | const FULL_DEPLOY = process.env.FULL_DEPLOY === 'true';
FILE: helpers/types.ts
type tEthereumAddress (line 1) | type tEthereumAddress = string;
type tStringTokenSmallUnits (line 2) | type tStringTokenSmallUnits = string;
FILE: test/helpers/constants.ts
constant INITIALIZED (line 1) | const INITIALIZED = 'Contract instance has already been initialized';
constant ZERO_ADDRESS_NOT_VALID (line 2) | const ZERO_ADDRESS_NOT_VALID = 'ZERO_ADDRESS_NOT_VALID';
constant CALLER_NOT_DISCOUNT_TOKEN (line 3) | const CALLER_NOT_DISCOUNT_TOKEN = 'CALLER_NOT_DISCOUNT_TOKEN';
constant CALLER_NOT_A_TOKEN (line 4) | const CALLER_NOT_A_TOKEN = 'CALLER_NOT_A_TOKEN';
FILE: test/helpers/make-suite.ts
type SignerWithAddress (line 51) | interface SignerWithAddress {
type TestEnv (line 56) | interface TestEnv {
function initializeMakeSuite (line 125) | async function initializeMakeSuite() {
function makeSuite (line 220) | function makeSuite(name: string, tests: (testEnv: TestEnv) => void) {
FILE: test/helpers/math/wadraymath.ts
type BigNumber (line 14) | interface BigNumber {
FILE: test/helpers/tokenization-events.ts
constant GHO_VARIABLE_DEBT_TOKEN_EVENTS (line 5) | const GHO_VARIABLE_DEBT_TOKEN_EVENTS = [
Condensed preview — 307 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,593K chars).
[
{
"path": ".github/CODEOWNERS",
"chars": 25,
"preview": "* @miguelmtzinf @foodaka\n"
},
{
"path": ".github/workflows/certora-gho-505.yml",
"chars": 1322,
"preview": "name: certora-gho-5.0.5\n\non:\n push:\n branches:\n - main\n pull_request:\n branches:\n - main\n\n workflow_d"
},
{
"path": ".github/workflows/certora-gho.yml",
"chars": 3745,
"preview": "name: certora-gho\n\non:\n push:\n branches:\n - main\n pull_request:\n branches:\n - main\n\n workflow_dispatc"
},
{
"path": ".github/workflows/certora-gsm-4626.yml",
"chars": 4069,
"preview": "name: certora-gsm-4626\n\non:\n push:\n branches:\n - main\n pull_request:\n branches:\n - main\n\n workflow_di"
},
{
"path": ".github/workflows/certora-gsm.yml",
"chars": 2734,
"preview": "name: certora-gsm\n\non:\n push:\n branches:\n - main\n pull_request:\n branches:\n - main\n\n workflow_dispatc"
},
{
"path": ".github/workflows/certora-steward.yml",
"chars": 1349,
"preview": "name: certora-steward\n\non:\n push:\n branches:\n - main\n pull_request:\n branches:\n - main\n\n workflow_dis"
},
{
"path": ".github/workflows/dependency-review.yml",
"chars": 441,
"preview": "name: Dependency Review\non:\n - pull_request\n\npermissions:\n contents: read\n pull-requests: write\n\njobs:\n dependency-r"
},
{
"path": ".github/workflows/node.js.yml",
"chars": 1065,
"preview": "name: Build\non: push\njobs:\n build:\n runs-on:\n group: larger\n env:\n ALCHEMY_KEY: '${{secrets.ALCHEMY_KEY"
},
{
"path": ".github/workflows/sync-issue.yml",
"chars": 568,
"preview": "name: Sync Issue to Height\n\non:\n issues:\n types: [opened]\n\njobs:\n sync:\n runs-on: ubuntu-latest\n steps:\n "
},
{
"path": ".gitignore",
"chars": 224,
"preview": "/cache\n/cache_forge\n/out\n/artifacts\n/.env\n/node_modules\n\n/deployments\n/types\n\n/coverage\n/coverage.json\n\n/temp-artifacts\n"
},
{
"path": ".gitmodules",
"chars": 1000,
"preview": "[submodule \"lib/forge-std\"]\n\tpath = lib/forge-std\n\turl = https://github.com/foundry-rs/forge-std\n\tbranch = v1.3.0\n[submo"
},
{
"path": ".husky/.gitignore",
"chars": 2,
"preview": "_\n"
},
{
"path": ".husky/pre-commit",
"chars": 58,
"preview": "#!/bin/sh\n. \"$(dirname \"$0\")/_/husky.sh\"\n\nnpx lint-staged\n"
},
{
"path": ".prettierignore",
"chars": 75,
"preview": "artifacts\ncache\nnode_modules\ntypes\nout\ndeployments\nlib\ncoverage\ncache_forge"
},
{
"path": ".prettierrc",
"chars": 239,
"preview": "{\n \"printWidth\": 100,\n \"trailingComma\": \"es5\",\n \"semi\": true,\n \"singleQuote\": true,\n \"tabWidth\": 2,\n \"overrides\": "
},
{
"path": ".solcover.js",
"chars": 133,
"preview": "module.exports = {\n skipFiles: ['./script', './test'],\n mocha: {\n enableTimeouts: false,\n },\n configureYulOptimiz"
},
{
"path": ".vscode/settings.json",
"chars": 161,
"preview": "{\n \"solidity.formatter\": \"none\",\n \"editor.defaultFormatter\": \"esbenp.prettier-vscode\",\n \"prettier.configPath\": \".pret"
},
{
"path": "LICENSE",
"chars": 1061,
"preview": "MIT License\n\nCopyright (c) 2023 Aave\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof th"
},
{
"path": "README.md",
"chars": 4116,
"preview": "# ⚠️ This repository is DEPRECATED and no longer maintained ⚠️\n\nFor the latest GHO code visit the GHO Origin Repository "
},
{
"path": "certora/gho/Makefile",
"chars": 709,
"preview": "default: help\n\nPATCH = applyHarness.patch\nCONTRACTS_DIR = ../../src\nMUNGED_DIR = munged\n\nhelp:\n\t@echo \"usage:"
},
{
"path": "certora/gho/applyHarness.patch",
"chars": 5311,
"preview": "diff -ruN ../../src/contracts/gho/GhoToken.sol contracts/gho/GhoToken.sol\n--- ../../src/contracts/gho/GhoToken.sol\t2024-"
},
{
"path": "certora/gho/conf/verifyFlashMinter.conf",
"chars": 980,
"preview": "{\n \"files\": [\n \"certora/gho/munged/contracts/facilitators/flashMinter/GhoFlashMinter.sol:GhoFlashMinter\",\n "
},
{
"path": "certora/gho/conf/verifyGhoAToken.conf",
"chars": 962,
"preview": "{\n \"files\": [\n \"certora/gho/munged/contracts/facilitators/aave/tokens/GhoAToken.sol\",\n \"certora/gho/mun"
},
{
"path": "certora/gho/conf/verifyGhoDiscountRateStrategy.conf",
"chars": 662,
"preview": "{\n \"files\": [\n \"certora/gho/harness/GhoDiscountRateStrategyHarness.sol:GhoDiscountRateStrategyHarness\"\n ],\n"
},
{
"path": "certora/gho/conf/verifyGhoToken.conf",
"chars": 495,
"preview": "{\n \"files\": [\n \"certora/gho/harness/GhoTokenHarness.sol:GhoTokenHarness\",\n ],\n \"packages\": [\n \"@a"
},
{
"path": "certora/gho/conf/verifyGhoVariableDebtToken-rayMulDiv-summarization.conf",
"chars": 1220,
"preview": "{\n \"files\": [\n \"certora/gho/harness/ghoVariableDebtTokenHarness.sol:GhoVariableDebtTokenHarness\",\n \"cer"
},
{
"path": "certora/gho/conf/verifyGhoVariableDebtToken.conf",
"chars": 1183,
"preview": "{\n \"files\": [\n \"certora/gho/harness/ghoVariableDebtTokenHarness.sol:GhoVariableDebtTokenHarness\",\n \"cer"
},
{
"path": "certora/gho/conf/verifyGhoVariableDebtTokenInternal.conf",
"chars": 789,
"preview": "{\n \"files\": [\n \"certora/gho/harness/ghoVariableDebtTokenHarnessInternal.sol:GhoVariableDebtTokenHarnessInterna"
},
{
"path": "certora/gho/conf/verifyGhoVariableDebtToken_specialBranch.conf",
"chars": 1244,
"preview": "{\n \"files\": [\n \"certora/gho/harness/ghoVariableDebtTokenHarness.sol:GhoVariableDebtTokenHarness\",\n \"cer"
},
{
"path": "certora/gho/conf/verifyGhoVariableDebtToken_summarized.conf",
"chars": 1194,
"preview": "{\n \"files\": [\n \"certora/gho/harness/ghoVariableDebtTokenHarness.sol:GhoVariableDebtTokenHarness\",\n \"cer"
},
{
"path": "certora/gho/conf/verifyUpgradeableGhoToken.conf",
"chars": 450,
"preview": "{\n \"files\": [\n \"certora/gho/harness/UpgradeableGhoTokenHarness.sol:UpgradeableGhoTokenHarness\",\n ],\n \"pa"
},
{
"path": "certora/gho/harness/DummyERC20A.sol",
"chars": 98,
"preview": "pragma solidity ^0.8.0;\nimport './DummyERC20Impl.sol';\n\ncontract DummyERC20A is DummyERC20Impl {}\n"
},
{
"path": "certora/gho/harness/DummyERC20B.sol",
"chars": 98,
"preview": "pragma solidity ^0.8.0;\nimport './DummyERC20Impl.sol';\n\ncontract DummyERC20B is DummyERC20Impl {}\n"
},
{
"path": "certora/gho/harness/DummyERC20Impl.sol",
"chars": 1495,
"preview": "// SPDX-License-Identifier: agpl-3.0\npragma solidity ^0.8.0;\n\n// with mint\ncontract DummyERC20Impl {\n uint256 t;\n mapp"
},
{
"path": "certora/gho/harness/DummyERC20WithTimedBalanceOf.sol",
"chars": 351,
"preview": "contract DummyERC20WithTimedBalanceOf {\n function balanceOf(address user) public view virtual returns (uint256) {\n r"
},
{
"path": "certora/gho/harness/DummyPool.sol",
"chars": 418,
"preview": "contract DummyPool {\n function getReserveNormalizedVariableDebt(address asset) external view returns (uint256) {\n re"
},
{
"path": "certora/gho/harness/GhoDiscountRateStrategyHarness.sol",
"chars": 422,
"preview": "import {GhoDiscountRateStrategy} from '../munged/contracts/facilitators/aave/interestStrategy/GhoDiscountRateStrategy.so"
},
{
"path": "certora/gho/harness/GhoTokenHarness.sol",
"chars": 2726,
"preview": "pragma solidity ^0.8.0;\n\nimport {IGhoToken} from '../munged/contracts/gho/interfaces/IGhoToken.sol';\nimport '@openzeppel"
},
{
"path": "certora/gho/harness/MockFlashBorrower.sol",
"chars": 5040,
"preview": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\nimport {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC"
},
{
"path": "certora/gho/harness/UpgradeableGhoTokenHarness.sol",
"chars": 2771,
"preview": "pragma solidity ^0.8.0;\n\nimport {IGhoToken} from '../munged/contracts/gho/interfaces/IGhoToken.sol';\nimport '@openzeppel"
},
{
"path": "certora/gho/harness/ghoVariableDebtTokenHarness.sol",
"chars": 1464,
"preview": "pragma solidity 0.8.10;\n\nimport {GhoVariableDebtToken} from '../munged/contracts/facilitators/aave/tokens/GhoVariableDeb"
},
{
"path": "certora/gho/harness/ghoVariableDebtTokenHarnessInternal.sol",
"chars": 707,
"preview": "pragma solidity 0.8.10;\n\nimport {GhoVariableDebtTokenHarness} from './ghoVariableDebtTokenHarness.sol';\nimport {GhoVaria"
},
{
"path": "certora/gho/munged/.gitignore",
"chars": 14,
"preview": "*\n!.gitignore\n"
},
{
"path": "certora/gho/specs/VariableDebtToken.spec",
"chars": 15138,
"preview": "methods {\n\t// summarization for elimination the raymul operation in balance of and totalSupply.\n\t//getReserveNormalizedV"
},
{
"path": "certora/gho/specs/erc20.spec",
"chars": 777,
"preview": "// erc20 methods\nmethods {\n function _.name() external => DISPATCHER(true);\n funct"
},
{
"path": "certora/gho/specs/flashMinter.spec",
"chars": 6448,
"preview": "using GhoToken as gho;\nusing GhoAToken as atoken;\nusing MockFlashBorrower as flashBorrower;\n\nmethods{\n function _.isP"
},
{
"path": "certora/gho/specs/ghoAToken.spec",
"chars": 4513,
"preview": "import \"erc20.spec\";\n\nusing GhoTokenHarness as _ghoTokenHarness;\n\nmethods{\n\n\tfunction totalSupply() external returns (ui"
},
{
"path": "certora/gho/specs/ghoDiscountRateStrategy.spec",
"chars": 3975,
"preview": "methods {\n function calculateDiscountRate(uint256, uint256) external returns (uint256) envfree;\n function MIN_DISC"
},
{
"path": "certora/gho/specs/ghoToken.spec",
"chars": 15126,
"preview": "import \"set.spec\";\n\nmethods{\n\tfunction mint(address,uint256) external;\n\tfunction burn(uint256) external;\n\tfunction remov"
},
{
"path": "certora/gho/specs/ghoVariableDebtToken-rayMulDiv-summarization.spec",
"chars": 11458,
"preview": "//import \"erc20.spec\"\nimport \"VariableDebtToken.spec\";\nimport \"summarizations.spec\";\n\n\nusing GhoDiscountRateStrategy as "
},
{
"path": "certora/gho/specs/ghoVariableDebtToken.spec",
"chars": 29077,
"preview": "//import \"erc20.spec\"\nimport \"VariableDebtToken.spec\";\nimport \"summarizations.spec\";\n\n\nusing GhoDiscountRateStrategy as "
},
{
"path": "certora/gho/specs/ghoVariableDebtTokenInternal.spec",
"chars": 1199,
"preview": "import \"ghoVariableDebtToken.spec\";\n\nmethods{\n}\n\n\n// check a scenario that function _accrueDebtOnAction() returns non ze"
},
{
"path": "certora/gho/specs/ghoVariableDebtToken_summarized.spec",
"chars": 2269,
"preview": "import \"ghoVariableDebtToken.spec\";\n\nmethods{\n\tfunction GhoVariableDebtToken._accrueDebtOnAction(address user, uint256, "
},
{
"path": "certora/gho/specs/set-natspec.json",
"chars": 18245,
"preview": "[\n {\n \"content\": \"/**\\n * @title get Set array length\\n * @dev user should define getLen() in Solidity harness file."
},
{
"path": "certora/gho/specs/set.spec",
"chars": 11426,
"preview": "\nmethods{\n function getFacilitatorsListLen() external returns (uint256) envfree;\n}\n\ndefinition MAX_UINT256() returns "
},
{
"path": "certora/gho/specs/summarizations.spec",
"chars": 767,
"preview": "// Definition of RAY unit\nfunction first_term(uint256 x, uint256 y) returns uint256 { return x; }\n\nghost mapping(uint256"
},
{
"path": "certora/gsm/conf/gsm/FixedFeeStrategy.conf",
"chars": 738,
"preview": "{\n \"files\": [\n \"certora/gsm/harness/FixedFeeStrategyHarness.sol\",\n ],\n \"packages\": [\n \"@aave/core"
},
{
"path": "certora/gsm/conf/gsm/OracleSwapFreezer.conf",
"chars": 839,
"preview": "{\n \"files\": [\n \"certora/gsm/harness/OracleSwapFreezerHarness.sol\",\n \"src/contracts/facilitators/gsm/swa"
},
{
"path": "certora/gsm/conf/gsm/balances-buy.conf",
"chars": 1154,
"preview": "{\n \"files\": [\n \"certora/gsm/harness/GsmHarness.sol\",\n \"certora/gsm/harness/DummyERC20A.sol\",\n \"c"
},
{
"path": "certora/gsm/conf/gsm/balances-sell.conf",
"chars": 1156,
"preview": "{\n \"files\": [\n \"certora/gsm/harness/GsmHarness.sol\",\n \"certora/gsm/harness/DummyERC20A.sol\",\n \"c"
},
{
"path": "certora/gsm/conf/gsm/fees-buy.conf",
"chars": 1301,
"preview": "{\n \"files\": [\n \"certora/gsm/harness/GsmHarness.sol\",\n \"certora/gsm/harness/DummyERC20A.sol\",\n \"c"
},
{
"path": "certora/gsm/conf/gsm/fees-sell.conf",
"chars": 1286,
"preview": "{\n \"files\": [\n \"certora/gsm/harness/GsmHarness.sol\",\n \"certora/gsm/harness/DummyERC20A.sol\",\n \"c"
},
{
"path": "certora/gsm/conf/gsm/finishedRules.conf",
"chars": 1224,
"preview": "{\n \"files\": [\n \"certora/gsm/harness/GsmHarness.sol\",\n \"certora/gsm/harness/DummyERC20A.sol\",\n \"c"
},
{
"path": "certora/gsm/conf/gsm/getAmount_properties.conf",
"chars": 1213,
"preview": "{\n \"files\": [\n \"certora/gsm/harness/GsmHarness.sol\",\n \"certora/gsm/harness/DummyERC20A.sol\",\n \"c"
},
{
"path": "certora/gsm/conf/gsm/gho-assetToGhoInvertibility.conf",
"chars": 1268,
"preview": "{\n \"files\": [\n \"certora/gsm/harness/GsmHarness.sol\",\n \"certora/gsm/harness/DummyERC20A.sol\",\n \"c"
},
{
"path": "certora/gsm/conf/gsm/gho-fixedPriceStrategy.conf",
"chars": 814,
"preview": "{\n \"files\": [\n \"certora/gsm/harness/FixedPriceStrategyHarness.sol\",\n ],\n \"packages\": [\n \"@aave/co"
},
{
"path": "certora/gsm/conf/gsm/gho-gsm-2.conf",
"chars": 1306,
"preview": "{\n \"files\": [\n \"certora/gsm/harness/GsmHarness.sol\",\n \"certora/gsm/harness/DummyERC20A.sol\",\n \"c"
},
{
"path": "certora/gsm/conf/gsm/gho-gsm.conf",
"chars": 1222,
"preview": "{\n \"files\": [\n \"certora/gsm/harness/GsmHarness.sol\",\n \"certora/gsm/harness/DummyERC20A.sol\",\n \"c"
},
{
"path": "certora/gsm/conf/gsm/gho-gsm_inverse.conf",
"chars": 1185,
"preview": "{\n \"files\": [\n \"certora/gsm/harness/GsmHarness.sol\",\n \"certora/gsm/harness/DummyERC20A.sol\",\n \"c"
},
{
"path": "certora/gsm/conf/gsm/optimality.conf",
"chars": 1207,
"preview": "{\n \"files\": [\n \"certora/gsm/harness/GsmHarness.sol\",\n \"certora/gsm/harness/DummyERC20A.sol\",\n \"c"
},
{
"path": "certora/gsm/conf/gsm4626/balances-buy-4626.conf",
"chars": 1231,
"preview": "{\n \"files\": [\n \"certora/gsm/harness/Gsm4626Harness.sol\",\n \"certora/gsm/harness/DummyERC20A.sol\",\n "
},
{
"path": "certora/gsm/conf/gsm4626/balances-sell-4626.conf",
"chars": 1266,
"preview": "{\n \"files\": [\n \"certora/gsm/harness/Gsm4626Harness.sol\",\n \"certora/gsm/harness/DummyERC20A.sol\",\n "
},
{
"path": "certora/gsm/conf/gsm4626/fees-buy-4626.conf",
"chars": 1208,
"preview": "{\n \"files\": [\n \"certora/gsm/harness/Gsm4626Harness.sol\",\n \"certora/gsm/harness/DummyERC20A.sol\",\n "
},
{
"path": "certora/gsm/conf/gsm4626/fees-sell-4626.conf",
"chars": 1181,
"preview": "{\n \"files\": [\n \"certora/gsm/harness/Gsm4626Harness.sol\",\n \"certora/gsm/harness/DummyERC20A.sol\",\n "
},
{
"path": "certora/gsm/conf/gsm4626/finishedRules4626.conf",
"chars": 1336,
"preview": "{\n \"files\": [\n \"certora/gsm/harness/Gsm4626Harness.sol\",\n \"certora/gsm/harness/DummyERC20A.sol\",\n "
},
{
"path": "certora/gsm/conf/gsm4626/getAmount_4626_properties.conf",
"chars": 1257,
"preview": "{\n \"files\": [\n \"certora/gsm/harness/Gsm4626Harness.sol\",\n \"certora/gsm/harness/DummyERC20A.sol\",\n "
},
{
"path": "certora/gsm/conf/gsm4626/gho-assetToGhoInvertibility4626.conf",
"chars": 1296,
"preview": "{\n \"files\": [\n \"certora/gsm/harness/Gsm4626Harness.sol\",\n \"certora/gsm/harness/DummyERC20A.sol\",\n "
},
{
"path": "certora/gsm/conf/gsm4626/gho-fixedPriceStrategy4626.conf",
"chars": 830,
"preview": "{\n \"files\": [\n \"certora/gsm/harness/FixedPriceStrategy4626Harness.sol\",\n ],\n \"packages\": [\n \"@aav"
},
{
"path": "certora/gsm/conf/gsm4626/gho-gsm4626-2.conf",
"chars": 1349,
"preview": "{\n \"files\": [\n \"certora/gsm/harness/Gsm4626Harness.sol\",\n \"certora/gsm/harness/DummyERC20A.sol\",\n "
},
{
"path": "certora/gsm/conf/gsm4626/gho-gsm4626.conf",
"chars": 1258,
"preview": "{\n \"files\": [\n \"certora/gsm/harness/Gsm4626Harness.sol\",\n \"certora/gsm/harness/DummyERC20A.sol\",\n "
},
{
"path": "certora/gsm/conf/gsm4626/gho-gsm_4626_inverse.conf",
"chars": 1210,
"preview": "{\n \"files\": [\n \"certora/gsm/harness/Gsm4626Harness.sol\",\n \"certora/gsm/harness/DummyERC20A.sol\",\n "
},
{
"path": "certora/gsm/conf/gsm4626/optimality4626.conf",
"chars": 1247,
"preview": "{\n \"files\": [\n \"certora/gsm/harness/Gsm4626Harness.sol\",\n \"certora/gsm/harness/DummyERC20A.sol\",\n "
},
{
"path": "certora/gsm/harness/DiffHelper.sol",
"chars": 269,
"preview": "// SPDX-License-Identifier: UNLICENSED\n\npragma solidity ^0.8.0;\n\ncontract DiffHelper {\n function differsByAtMostN(uint2"
},
{
"path": "certora/gsm/harness/DummyERC20A.sol",
"chars": 98,
"preview": "pragma solidity ^0.8.0;\nimport './DummyERC20Impl.sol';\n\ncontract DummyERC20A is DummyERC20Impl {}\n"
},
{
"path": "certora/gsm/harness/DummyERC20B.sol",
"chars": 98,
"preview": "pragma solidity ^0.8.0;\nimport './DummyERC20Impl.sol';\n\ncontract DummyERC20B is DummyERC20Impl {}\n"
},
{
"path": "certora/gsm/harness/DummyERC20Impl.sol",
"chars": 1495,
"preview": "// SPDX-License-Identifier: agpl-3.0\npragma solidity ^0.8.0;\n\n// with mint\ncontract DummyERC20Impl {\n uint256 t;\n mapp"
},
{
"path": "certora/gsm/harness/ERC20Helper.sol",
"chars": 425,
"preview": "// SPDX-License-Identifier: agpl-3.0\npragma solidity ^0.8.0;\nimport {IERC20} from '@aave/core-v3/contracts/dependencies/"
},
{
"path": "certora/gsm/harness/FixedFeeStrategyHarness.sol",
"chars": 668,
"preview": "pragma solidity ^0.8.0;\n\nimport {FixedFeeStrategy} from '../../../src/contracts/facilitators/gsm/feeStrategy/FixedFeeStr"
},
{
"path": "certora/gsm/harness/FixedPriceStrategy4626Harness.sol",
"chars": 602,
"preview": "pragma solidity ^0.8.0;\n\nimport {FixedPriceStrategy4626} from '../../../src/contracts/facilitators/gsm/priceStrategy/Fix"
},
{
"path": "certora/gsm/harness/FixedPriceStrategyHarness.sol",
"chars": 699,
"preview": "pragma solidity ^0.8.0;\n\nimport {FixedPriceStrategy} from '../../../src/contracts/facilitators/gsm/priceStrategy/FixedPr"
},
{
"path": "certora/gsm/harness/Gsm4626Harness.sol",
"chars": 4490,
"preview": "pragma solidity ^0.8.0;\n\nimport {Gsm4626} from '../../../src/contracts/facilitators/gsm/Gsm4626.sol';\nimport {IGhoToken}"
},
{
"path": "certora/gsm/harness/GsmHarness.sol",
"chars": 3603,
"preview": "pragma solidity ^0.8.0;\n\nimport {Gsm} from '../../../src/contracts/facilitators/gsm/Gsm.sol';\nimport {IGhoToken} from '."
},
{
"path": "certora/gsm/harness/OracleSwapFreezerHarness.sol",
"chars": 2450,
"preview": "pragma solidity ^0.8.0;\n\nimport {OracleSwapFreezer} from '../../../src/contracts/facilitators/gsm/swapFreezer/OracleSwap"
},
{
"path": "certora/gsm/munged/.gitignore",
"chars": 14,
"preview": "*\n!.gitignore\n"
},
{
"path": "certora/gsm/specs/GsmMethods/aave_fee_limits.spec",
"chars": 203,
"preview": "function feeLimits(env e) {\n require currentContract.getSellFeeBP(e) <= 5000 && currentContract.getBuyFeeBP(e) < 5000"
},
{
"path": "certora/gsm/specs/GsmMethods/aave_price_fee_limits.spec",
"chars": 64,
"preview": "import \"aave_price_limits.spec\";\nimport \"aave_fee_limits.spec\";\n"
},
{
"path": "certora/gsm/specs/GsmMethods/aave_price_fee_limits_strict.spec",
"chars": 423,
"preview": "function feeLimits(env e) {\n require currentContract.getSellFeeBP(e) <= 1000 && currentContract.getBuyFeeBP(e) < 1000"
},
{
"path": "certora/gsm/specs/GsmMethods/aave_price_limits.spec",
"chars": 218,
"preview": "function priceLimits(env e) {\n uint8 exp;\n require 5 <= exp;\n require exp <= 27;\n require getUnderlyingAsset"
},
{
"path": "certora/gsm/specs/GsmMethods/erc20.spec",
"chars": 776,
"preview": "// ERC20 methods\nmethods {\n function _.name() external => DISPATCHER(true);\n functi"
},
{
"path": "certora/gsm/specs/GsmMethods/erc4626.spec",
"chars": 587,
"preview": "methods {\n function _.previewWithdraw(uint256 vaultAssets) external with (env e) =>\n mulDivSummaryRounding(vau"
},
{
"path": "certora/gsm/specs/GsmMethods/methods4626_base.spec",
"chars": 3835,
"preview": "import \"./erc20.spec\";\n\n\nusing FixedPriceStrategy4626Harness as _priceStrategy;\nusing FixedFeeStrategyHarness as _FixedF"
},
{
"path": "certora/gsm/specs/GsmMethods/methods_base-Martin.spec",
"chars": 4527,
"preview": "import \"./erc20.spec\";\n\nusing GhoToken as _ghoToken;\nusing ERC20Helper as erc20Helper;\n\n\n/////////////////// Methods ///"
},
{
"path": "certora/gsm/specs/GsmMethods/methods_base.spec",
"chars": 5182,
"preview": "import \"./erc20.spec\";\n\nusing FixedPriceStrategyHarness as _priceStrategy;\nusing FixedFeeStrategyHarness as _FixedFeeStr"
},
{
"path": "certora/gsm/specs/GsmMethods/methods_divint_summary.spec",
"chars": 894,
"preview": "// The (unverified) summary for OpenZeppelin's `Math.mulDiv`.\n// Use with care!\nmethods\n{\n function Math.mulDiv(uint2"
},
{
"path": "certora/gsm/specs/GsmMethods/shared.spec",
"chars": 2167,
"preview": "import \"../GsmMethods/methods_base-Martin.spec\";\nimport \"../GsmMethods/methods_divint_summary.spec\";\nimport \"../GsmMetho"
},
{
"path": "certora/gsm/specs/gsm/AssetToGhoInvertibility.spec",
"chars": 11511,
"preview": "import \"../GsmMethods/methods_base.spec\";\n\n\n\n\nmethods {\n function _.mulDiv(uint256 x, uint256 y, uint256 denominator)"
},
{
"path": "certora/gsm/specs/gsm/FixedFeeStrategy.spec",
"chars": 5818,
"preview": "// verifies properties of FixedFeestrategy\n\nimport \"../GsmMethods/aave_fee_limits.spec\";\nimport \"../GsmMethods/methods_d"
},
{
"path": "certora/gsm/specs/gsm/FixedPriceStrategy.spec",
"chars": 5954,
"preview": "// import \"../GsmMethods/methods_base.spec\";\n\n\nmethods {\n function getAssetPriceInGho(uint256, bool) external returns"
},
{
"path": "certora/gsm/specs/gsm/OracleSwapFreezer.spec",
"chars": 3552,
"preview": "\n// verifies properties of OracleSwapFreezer\n\nmethods {\n\tfunction getFreezeBound() external returns (uint128, uint128) e"
},
{
"path": "certora/gsm/specs/gsm/balances-buy.spec",
"chars": 15131,
"preview": "import \"../GsmMethods/erc20.spec\";\nimport \"../GsmMethods/methods_divint_summary.spec\";\nimport \"../GsmMethods/aave_price_"
},
{
"path": "certora/gsm/specs/gsm/balances-sell.spec",
"chars": 6855,
"preview": "import \"../GsmMethods/erc20.spec\";\nimport \"../GsmMethods/methods_divint_summary.spec\";\nimport \"../GsmMethods/aave_price_"
},
{
"path": "certora/gsm/specs/gsm/fees-buy.spec",
"chars": 6704,
"preview": "import \"../GsmMethods/erc20.spec\";\nimport \"../GsmMethods/methods_divint_summary.spec\";\nimport \"../GsmMethods/aave_price_"
},
{
"path": "certora/gsm/specs/gsm/fees-sell.spec",
"chars": 6732,
"preview": "import \"../GsmMethods/erc20.spec\";\nimport \"../GsmMethods/methods_divint_summary.spec\";\nimport \"../GsmMethods/aave_price_"
},
{
"path": "certora/gsm/specs/gsm/getAmount_properties.spec",
"chars": 19161,
"preview": "import \"../GsmMethods/methods_base.spec\";\nimport \"../GsmMethods/aave_price_fee_limits.spec\";\nimport \"../GsmMethods/metho"
},
{
"path": "certora/gsm/specs/gsm/gho-gsm-2.spec",
"chars": 6746,
"preview": "import \"../GsmMethods/shared.spec\";\n\nusing GhoToken as _ghoTokenHook;\nusing DummyERC20B as UNDERLYING_ASSET;\n\nusing Fixe"
},
{
"path": "certora/gsm/specs/gsm/gho-gsm-Buy.spec",
"chars": 6792,
"preview": "import \"../GsmMethods/methods_base.spec\";\nimport \"../GsmMethods/aave_price_fee_limits.spec\";\n\n// patch2: violated by at "
},
{
"path": "certora/gsm/specs/gsm/gho-gsm-finishedRules.spec",
"chars": 15488,
"preview": "import \"../GsmMethods/methods_base.spec\";\nimport \"../GsmMethods/aave_price_fee_limits.spec\";\nimport \"../GsmMethods/metho"
},
{
"path": "certora/gsm/specs/gsm/gho-gsm.spec",
"chars": 7387,
"preview": "import \"../GsmMethods/methods_base.spec\";\nimport \"../GsmMethods/methods_divint_summary.spec\";\nimport \"../GsmMethods/aave"
},
{
"path": "certora/gsm/specs/gsm/gho-gsm_inverse.spec",
"chars": 30260,
"preview": "import \"../GsmMethods/methods_base.spec\";\nimport \"../GsmMethods/methods_divint_summary.spec\";\n\n// @title Buy/sell invari"
},
{
"path": "certora/gsm/specs/gsm/optimality.spec",
"chars": 10231,
"preview": "import \"../GsmMethods/methods_base.spec\";\nimport \"../GsmMethods/aave_price_fee_limits.spec\";\nimport \"../GsmMethods/metho"
},
{
"path": "certora/gsm/specs/gsm4626/AssetToGhoInvertibility4626.spec",
"chars": 12611,
"preview": "import \"../GsmMethods/methods4626_base.spec\";\nimport \"../GsmMethods/erc4626.spec\";\n\n\n\nmethods {\n function _.mulDiv(ui"
},
{
"path": "certora/gsm/specs/gsm4626/FixedPriceStrategy4626.spec",
"chars": 4991,
"preview": "// import \"../GsmMethods/methods_base.spec\";\nimport \"../GsmMethods/erc4626.spec\";\n\n\nmethods {\n function getAssetPrice"
},
{
"path": "certora/gsm/specs/gsm4626/balances-buy-4626.spec",
"chars": 9620,
"preview": "import \"../GsmMethods/erc20.spec\";\nimport \"../GsmMethods/methods_divint_summary.spec\";\nimport \"../GsmMethods/aave_price_"
},
{
"path": "certora/gsm/specs/gsm4626/balances-sell-4626.spec",
"chars": 7876,
"preview": "import \"../GsmMethods/erc20.spec\";\nimport \"../GsmMethods/methods_divint_summary.spec\";\nimport \"../GsmMethods/aave_price_"
},
{
"path": "certora/gsm/specs/gsm4626/fees-buy-4626.spec",
"chars": 7293,
"preview": "import \"../GsmMethods/erc20.spec\";\nimport \"../GsmMethods/methods_divint_summary.spec\";\nimport \"../GsmMethods/aave_price_"
},
{
"path": "certora/gsm/specs/gsm4626/fees-sell-4626.spec",
"chars": 9245,
"preview": "import \"../GsmMethods/erc20.spec\";\nimport \"../GsmMethods/methods_divint_summary.spec\";\nimport \"../GsmMethods/aave_price_"
},
{
"path": "certora/gsm/specs/gsm4626/getAmount_4626_properties.spec",
"chars": 14789,
"preview": "import \"../GsmMethods/methods4626_base.spec\";\nimport \"../GsmMethods/aave_price_fee_limits.spec\";\nimport \"../GsmMethods/m"
},
{
"path": "certora/gsm/specs/gsm4626/gho-gsm-finishedRules4626.spec",
"chars": 17143,
"preview": "import \"../GsmMethods/methods4626_base.spec\";\nimport \"../GsmMethods/aave_price_fee_limits.spec\";\nimport \"../GsmMethods/m"
},
{
"path": "certora/gsm/specs/gsm4626/gho-gsm4626-2.spec",
"chars": 8962,
"preview": "import \"../GsmMethods/shared.spec\";\nimport \"../GsmMethods/erc4626.spec\";\n\nusing GhoToken as _ghoTokenHook;\nusing DummyER"
},
{
"path": "certora/gsm/specs/gsm4626/gho-gsm4626.spec",
"chars": 7353,
"preview": "import \"../GsmMethods/methods4626_base.spec\";\nimport \"../GsmMethods/methods_divint_summary.spec\";\nimport \"../GsmMethods/"
},
{
"path": "certora/gsm/specs/gsm4626/gho-gsm_4626_inverse.spec",
"chars": 26708,
"preview": "import \"../GsmMethods/methods4626_base.spec\";\nimport \"../GsmMethods/methods_divint_summary.spec\";\nimport \"../GsmMethods/"
},
{
"path": "certora/gsm/specs/gsm4626/optimality4626.spec",
"chars": 10286,
"preview": "import \"../GsmMethods/methods4626_base.spec\";\nimport \"../GsmMethods/aave_price_fee_limits.spec\";\nimport \"../GsmMethods/m"
},
{
"path": "certora/steward/Makefile",
"chars": 725,
"preview": "default: help\n\nPATCH = applyHarness.patch\nCONTRACTS_DIR = ../../src\nMUNGED_DIR = munged\n\nhelp:\n\t@echo \"usage:"
},
{
"path": "certora/steward/applyHarness.patch",
"chars": 193,
"preview": "diff -ruN .gitignore .gitignore\n--- .gitignore\t1970-01-01 02:00:00.000000000 +0200\n+++ .gitignore\t2024-08-12 17:28:45.84"
},
{
"path": "certora/steward/conf/GhoAaveSteward.conf",
"chars": 1247,
"preview": "{\n \"files\": [\"certora/steward/harness/GhoAaveSteward_Harness.sol\"],\n \"packages\": [\n \"@aave/core-v3/"
},
{
"path": "certora/steward/conf/GhoBucketSteward.conf",
"chars": 1248,
"preview": "{\n \"files\": [\n \"src/contracts/misc/GhoBucketSteward.sol\"\n ],\n \"packages\": [\n \"@aave/core-"
},
{
"path": "certora/steward/conf/GhoCcipSteward.conf",
"chars": 1247,
"preview": "{\n \"files\": [\"certora/steward/harness/GhoCcipSteward_Harness.sol\"],\n \"packages\": [\n \"@aave/core-v3/"
},
{
"path": "certora/steward/conf/GhoGsmSteward.conf",
"chars": 1444,
"preview": "{\n \"files\": [\n \"certora/steward/harness/GhoGsmSteward_Harness.sol\",\n \"src/contracts/facilitators/gsm/fe"
},
{
"path": "certora/steward/harness/GhoAaveSteward_Harness.sol",
"chars": 535,
"preview": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\nimport {GhoAaveSteward} from '../munged/src/contracts/misc/Gho"
},
{
"path": "certora/steward/harness/GhoCcipSteward_Harness.sol",
"chars": 387,
"preview": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\nimport {GhoCcipSteward} from '../../../src/contracts/misc/GhoC"
},
{
"path": "certora/steward/harness/GhoGsmSteward_Harness.sol",
"chars": 325,
"preview": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\nimport {GhoGsmSteward} from '../../../src/contracts/misc/GhoGs"
},
{
"path": "certora/steward/harness/GhoStewardV2_Harness.sol",
"chars": 605,
"preview": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\nimport {GhoStewardV2} from '../../../src/contracts/misc/GhoSte"
},
{
"path": "certora/steward/munged/.gitignore",
"chars": 13,
"preview": "*\n!.gitignore"
},
{
"path": "certora/steward/specs/GhoAaveSteward.spec",
"chars": 9063,
"preview": "\n/*===========================================================================\n This is a specification file for the co"
},
{
"path": "certora/steward/specs/GhoBucketSteward.spec",
"chars": 5063,
"preview": "//using FixedRateStrategyFactory as FAC;\n\n\n/*==========================================================================="
},
{
"path": "certora/steward/specs/GhoCcipSteward.spec",
"chars": 6484,
"preview": "//using FixedFeeStrategyFactory as FAC;\n\n\n/*===========================================================================\n"
},
{
"path": "certora/steward/specs/GhoGsmSteward.spec",
"chars": 7241,
"preview": "using FixedFeeStrategyFactory as FAC;\n\n\n/*===========================================================================\n "
},
{
"path": "deploy/00_deploy_gho_token.ts",
"chars": 763,
"preview": "import { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport { DeployFunction } from 'hardhat-deploy/types';\n\nconst"
},
{
"path": "deploy/01_deploy_gho_oracle.ts",
"chars": 606,
"preview": "import { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport { DeployFunction } from 'hardhat-deploy/types';\n\nconst"
},
{
"path": "deploy/02_deploy_gho_atoken.ts",
"chars": 1163,
"preview": "import { DeployFunction } from 'hardhat-deploy/types';\nimport { getPool } from '@aave/deploy-v3/dist/helpers/contract-ge"
},
{
"path": "deploy/03_deploy_gho_stable_debt.ts",
"chars": 1148,
"preview": "import { DeployFunction } from 'hardhat-deploy/types';\nimport { getPool } from '@aave/deploy-v3/dist/helpers/contract-ge"
},
{
"path": "deploy/04_deploy_gho_variable_debt.ts",
"chars": 1168,
"preview": "import { DeployFunction } from 'hardhat-deploy/types';\nimport { getPool } from '@aave/deploy-v3/dist/helpers/contract-ge"
},
{
"path": "deploy/05_deploy_gho_interest_rate.ts",
"chars": 934,
"preview": "import { DeployFunction } from 'hardhat-deploy/types';\nimport { ghoReserveConfig } from '../helpers/config';\nimport { ge"
},
{
"path": "deploy/06_deploy_gho_discount_rate.ts",
"chars": 578,
"preview": "import { DeployFunction } from 'hardhat-deploy/types';\n\nconst func: DeployFunction = async function ({ getNamedAccounts,"
},
{
"path": "deploy/07_deploy_stakedAave_upgrade.ts",
"chars": 1119,
"preview": "import { DeployFunction } from 'hardhat-deploy/types';\nimport { StakedTokenV2Rev3__factory, STAKE_AAVE_PROXY, waitForTx "
},
{
"path": "deploy/08_deploy_gho_flashminter.ts",
"chars": 1161,
"preview": "import { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport"
},
{
"path": "deploy/09_deploy_uighodataprovider.ts",
"chars": 937,
"preview": "import { HardhatRuntimeEnvironment } from 'hardhat/types';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport"
},
{
"path": "docs/gho-stewards.md",
"chars": 2049,
"preview": "## Overview\n\nThese contracts each control different parameters related to GHO and its facilitators. They allow the Aave "
},
{
"path": "foundry.toml",
"chars": 396,
"preview": "[profile.default]\nsrc = 'src'\nout = 'out'\ntest = 'src/test'\nscript = 'src/script'\ncache_path = 'cache_forge'\nlibs = ['n"
},
{
"path": "hardhat.config.ts",
"chars": 2409,
"preview": "import { getCommonNetworkConfig, hardhatNetworkSettings } from './helpers/hardhat-config';\nimport { config } from 'doten"
},
{
"path": "helpers/config.ts",
"chars": 957,
"preview": "import { ethers } from 'ethers';\nimport { ZERO_ADDRESS } from './constants';\n\nexport const helperAddresses = {\n wethWha"
},
{
"path": "helpers/constants.ts",
"chars": 1535,
"preview": "// ----------------\n// MATH\n// ----------------\n\nimport { BigNumber } from 'ethers';\nimport { parseEther, parseUnits } f"
},
{
"path": "helpers/contract-getters.ts",
"chars": 4836,
"preview": "import { Contract } from 'ethers';\nimport { tEthereumAddress } from './types';\nimport { HardhatRuntimeEnvironment } from"
},
{
"path": "helpers/hardhat-config.ts",
"chars": 2450,
"preview": "import {\n DEFAULT_BLOCK_GAS_LIMIT,\n eEthereumNetwork,\n FORK,\n FORK_BLOCK_NUMBER,\n getAlchemyKey,\n} from '@aave/depl"
},
{
"path": "helpers/misc-utils.ts",
"chars": 3645,
"preview": "import { formatEther } from 'ethers/lib/utils';\nimport path from 'path';\nimport fs from 'fs';\nimport { BigNumber, Signer"
},
{
"path": "helpers/types.ts",
"chars": 142,
"preview": "export type tEthereumAddress = string;\nexport type tStringTokenSmallUnits = string; // 1 wei, or 1 basic unit of USDC, o"
},
{
"path": "package.json",
"chars": 3634,
"preview": "{\n \"name\": \"@aave/gho\",\n \"description\": \"GHO core smart contracts\",\n \"keywords\": [\n \"gho\",\n \"stablecoin\",\n \""
},
{
"path": "remappings.txt",
"chars": 774,
"preview": "@aave/core-v3/=lib/aave-v3-core/\n@aave/periphery-v3/=lib/aave-v3-periphery/\n@aave/=lib/aave-token/\n@openzeppelin/=lib/op"
},
{
"path": "setup-test-env.sh",
"chars": 662,
"preview": "\n#!/bin/bash\n\n# @dev\n# This bash script ensures a clean repository\n# and loads environment variables for testing and dep"
},
{
"path": "slither.config.json",
"chars": 102,
"preview": "{\n \"detectors_to_exclude\": \"naming-convention\",\n \"filter_paths\": \"(node_modules/|lib/|src/test/)\"\n}\n"
},
{
"path": "src/contracts/facilitators/aave/interestStrategy/FixedRateStrategyFactory.sol",
"chars": 3292,
"preview": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\nimport {IDefaultInterestRateStrategy} from '@aave/core-v3/cont"
},
{
"path": "src/contracts/facilitators/aave/interestStrategy/GhoDiscountRateStrategy.sol",
"chars": 1960,
"preview": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\nimport {WadRayMath} from '@aave/core-v3/contracts/protocol/lib"
},
{
"path": "src/contracts/facilitators/aave/interestStrategy/GhoInterestRateStrategy.sol",
"chars": 3225,
"preview": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\nimport {DataTypes} from '@aave/core-v3/contracts/protocol/libr"
},
{
"path": "src/contracts/facilitators/aave/interestStrategy/ZeroDiscountRateStrategy.sol",
"chars": 559,
"preview": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\nimport {IGhoDiscountRateStrategy} from '../interestStrategy/in"
},
{
"path": "src/contracts/facilitators/aave/interestStrategy/interfaces/IFixedRateStrategyFactory.sol",
"chars": 2033,
"preview": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n/**\n * @title IFixedRateStrategyFactory\n * @author Aave Labs\n *"
},
{
"path": "src/contracts/facilitators/aave/interestStrategy/interfaces/IGhoDiscountRateStrategy.sol",
"chars": 668,
"preview": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n/**\n * @title IGhoDiscountRateStrategy\n * @author Aave\n * @noti"
},
{
"path": "src/contracts/facilitators/aave/misc/UiGhoDataProvider.sol",
"chars": 3025,
"preview": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\nimport {IERC20} from '@aave/core-v3/contracts/dependencies/ope"
},
{
"path": "src/contracts/facilitators/aave/misc/interfaces/IUiGhoDataProvider.sol",
"chars": 1280,
"preview": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\n/**\n * @title IUiGhoDataProvider\n * @author Aave\n * @notice De"
},
{
"path": "src/contracts/facilitators/aave/oracle/GhoOracle.sol",
"chars": 731,
"preview": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\n/**\n * @title GhoOracle\n * @notice Price feed for GHO (USD den"
},
{
"path": "src/contracts/facilitators/aave/tokens/GhoAToken.sol",
"chars": 8792,
"preview": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\nimport {IERC20} from '@aave/core-v3/contracts/dependencies/ope"
},
{
"path": "src/contracts/facilitators/aave/tokens/GhoStableDebtToken.sol",
"chars": 5944,
"preview": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity ^0.8.10;\n\nimport {IERC20} from '@aave/core-v3/contracts/dependencie"
},
{
"path": "src/contracts/facilitators/aave/tokens/GhoVariableDebtToken.sol",
"chars": 18279,
"preview": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\nimport {IERC20} from '@aave/core-v3/contracts/dependencies/ope"
},
{
"path": "src/contracts/facilitators/aave/tokens/base/ScaledBalanceTokenBase.sol",
"chars": 4582,
"preview": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\nimport {SafeCast} from '@aave/core-v3/contracts/dependencies/o"
},
{
"path": "src/contracts/facilitators/aave/tokens/interfaces/IGhoAToken.sol",
"chars": 1007,
"preview": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {IAToken} from '@aave/core-v3/contracts/interfaces/IATok"
},
{
"path": "src/contracts/facilitators/aave/tokens/interfaces/IGhoVariableDebtToken.sol",
"chars": 4310,
"preview": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {IVariableDebtToken} from '@aave/core-v3/contracts/inter"
},
{
"path": "src/contracts/facilitators/flashMinter/GhoFlashMinter.sol",
"chars": 5649,
"preview": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {IACLManager} from '@aave/core-v3/contracts/interfaces/I"
},
{
"path": "src/contracts/facilitators/flashMinter/interfaces/IGhoFlashMinter.sol",
"chars": 2729,
"preview": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {IERC3156FlashLender} from '@openzeppelin/contracts/inte"
},
{
"path": "src/contracts/facilitators/gsm/Gsm.sol",
"chars": 19697,
"preview": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\nimport {VersionedInitializable} from '@aave/core-v3/contracts/"
},
{
"path": "src/contracts/facilitators/gsm/Gsm4626.sol",
"chars": 5660,
"preview": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\nimport {IERC20} from '@aave/core-v3/contracts/dependencies/ope"
},
{
"path": "src/contracts/facilitators/gsm/dependencies/chainlink/AutomationCompatibleInterface.sol",
"chars": 2308,
"preview": "// SPDX-License-Identifier: MIT\n// Chainlink Contracts v0.8\npragma solidity ^0.8.0;\n\ninterface AutomationCompatibleInter"
},
{
"path": "src/contracts/facilitators/gsm/feeStrategy/FixedFeeStrategy.sol",
"chars": 2497,
"preview": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\nimport {Math} from '@openzeppelin/contracts/utils/math/Math.so"
},
{
"path": "src/contracts/facilitators/gsm/feeStrategy/FixedFeeStrategyFactory.sol",
"chars": 3236,
"preview": "/// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\nimport {EnumerableSet} from '@openzeppelin/contracts/utils/st"
},
{
"path": "src/contracts/facilitators/gsm/feeStrategy/interfaces/IFixedFeeStrategyFactory.sol",
"chars": 1902,
"preview": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n/**\n * @title IFixedFeeStrategyFactory\n * @author Aave Labs\n * "
},
{
"path": "src/contracts/facilitators/gsm/feeStrategy/interfaces/IGsmFeeStrategy.sol",
"chars": 1714,
"preview": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n/**\n * @title IGsmFeeStrategy\n * @author Aave\n * @notice Define"
},
{
"path": "src/contracts/facilitators/gsm/interfaces/IGsm.sol",
"chars": 14316,
"preview": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {IAccessControl} from '@openzeppelin/contracts/access/IA"
},
{
"path": "src/contracts/facilitators/gsm/interfaces/IGsm4626.sol",
"chars": 1984,
"preview": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\nimport {IGsm} from './IGsm.sol';\n\n/**\n * @title IGsm4626\n * @au"
},
{
"path": "src/contracts/facilitators/gsm/misc/GsmRegistry.sol",
"chars": 1697,
"preview": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\nimport {EnumerableSet} from '@openzeppelin/contracts/utils/str"
},
{
"path": "src/contracts/facilitators/gsm/misc/IGsmRegistry.sol",
"chars": 1469,
"preview": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\n/**\n * @title IGsmRegistry\n * @author Aave\n * @notice Defines t"
},
{
"path": "src/contracts/facilitators/gsm/misc/SampleLiquidator.sol",
"chars": 1455,
"preview": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\nimport {Ownable} from '@openzeppelin/contracts/access/Ownable."
},
{
"path": "src/contracts/facilitators/gsm/misc/SampleSwapFreezer.sol",
"chars": 708,
"preview": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.10;\n\nimport {Ownable} from '@openzeppelin/contracts/access/Ownable."
}
]
// ... and 107 more files (download for full content)
About this extraction
This page contains the full source code of the aave/gho-core GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 307 files (1.5 MB), approximately 412.8k tokens, and a symbol index with 41 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.