Skip to content

Commit

Permalink
Merge pull request #874 from ajna-finance/develop
Browse files Browse the repository at this point in the history
Merge develop
  • Loading branch information
grandizzy committed Jun 3, 2023
2 parents 19cba1a + 989b5f2 commit b301603
Show file tree
Hide file tree
Showing 217 changed files with 15,117 additions and 3,085 deletions.
21 changes: 5 additions & 16 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ ETHERSCAN_API_KEY=
## for deployment ##
ALCHEMY_API_KEY=

## Infura API key
## Infura API key ##
WEB3_INFURA_PROJECT_ID=

## Ethereum node endpoint ##
Expand All @@ -19,21 +19,10 @@ ETH_FROM=
ETH_GAS=15000000

## Solidity Compiler Version ##
SOLC_VERSION=0.8.14
SOLC_VERSION=0.8.18

# AJNA token address for target chain
## AJNA token address for target chain ##
AJNA_TOKEN=0xaadebCF61AA7Da0573b524DE57c67aDa797D46c5

# path to the JSON keystore file for your deployment account
DEPLOY_KEY=

# Default token precisions for (invariant) testing
QUOTE_PRECISION = 18
COLLATERAL_PRECISION = 18

# Default bucket Index for (invariant) testing
BUCKET_INDEX_ERC20 = 2570
BUCKET_INDEX_ERC721 = 850

# Default no of buckets to use for (invariant) testing
NO_OF_BUCKETS = 3
## path to the JSON keystore file for your deployment account ##
DEPLOY_KEY=
8 changes: 1 addition & 7 deletions .github/workflows/forge-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,6 @@ env:

jobs:
check:
env:
QUOTE_PRECISION: 18
COLLATERAL_PRECISION: 18
BUCKET_INDEX_ERC20: 2570
BUCKET_INDEX_ERC721: 850
NO_OF_BUCKETS: 3
strategy:
fail-fast: true

Expand All @@ -37,5 +31,5 @@ jobs:

- name: Run tests
run: |
make test-with-gas-report && make test-regression
make test-with-gas-report && make test-regression-all
id: test
2 changes: 1 addition & 1 deletion .github/workflows/slither.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:

- name: Install and set solc version
run: |
pip install solc-select && solc-select install 0.8.14 && solc-select use 0.8.14
pip install solc-select && solc-select install 0.8.18 && solc-select use 0.8.18
id: solc

- name: Install Slither
Expand Down
9 changes: 8 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,11 @@ coverage/
*.info
report/
keystore/
broadcast/
broadcast/
logFile.txt

# Certora
.certora_internal/
.certora_recent_jobs.json
.zip-output-url.txt
*.zip
67 changes: 44 additions & 23 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
# include .env file and export its env vars
# (-include to ignore error if it does not exist)
-include .env
-include .env && source ./tests/forge/invariants/scenarios/scenario-${SCENARIO}.sh

# Default token precisions for invariant testing
QUOTE_PRECISION = 18
COLLATERAL_PRECISION = 18

# Default buckets for invariant testing
BUCKET_INDEX_ERC20 = 2570
BUCKET_INDEX_ERC721 = 850
NO_OF_BUCKETS = 3
CONTRACT_EXCLUDES="RegressionTest|Panic|RealWorld|Trading"
TEST_EXCLUDES="testLoad|invariant|test_regression"

all: clean install build

Expand All @@ -22,20 +16,47 @@ install :; git submodule update --init --recursive
# Builds
build :; forge clean && forge build

# Tests
test :; forge test --no-match-test "testLoad|invariant|test_regression" # --ffi # enable if you need the `ffi` cheat code on HEVM
test-with-gas-report :; forge test --no-match-test "testLoad|invariant|test_regression" --gas-report # --ffi # enable if you need the `ffi` cheat code on HEVM
test-load :; forge test --match-test testLoad --gas-report
test-invariant :; eval QUOTE_PRECISION=${QUOTE_PRECISION} COLLATERAL_PRECISION=${COLLATERAL_PRECISION} BUCKET_INDEX_ERC20=${BUCKET_INDEX_ERC20} BUCKET_INDEX_ERC721=${BUCKET_INDEX_ERC721} NO_OF_BUCKETS=${NO_OF_BUCKETS} forge t --mt invariant --nmc RegressionTest
test-invariant-erc20 :; eval QUOTE_PRECISION=${QUOTE_PRECISION} COLLATERAL_PRECISION=${COLLATERAL_PRECISION} BUCKET_INDEX_ERC20=${BUCKET_INDEX_ERC20} NO_OF_BUCKETS=${NO_OF_BUCKETS} forge t --mt invariant --nmc RegressionTest --mc ERC20
test-invariant-erc721 :; eval QUOTE_PRECISION=${QUOTE_PRECISION} BUCKET_INDEX_ERC721=${BUCKET_INDEX_ERC721} NO_OF_BUCKETS=${NO_OF_BUCKETS} forge t --mt invariant --nmc RegressionTest --mc ERC721
test-regression :; eval QUOTE_PRECISION=${QUOTE_PRECISION} COLLATERAL_PRECISION=${COLLATERAL_PRECISION} BUCKET_INDEX_ERC20=${BUCKET_INDEX_ERC20} BUCKET_INDEX_ERC721=${BUCKET_INDEX_ERC721} NO_OF_BUCKETS=${NO_OF_BUCKETS} forge t --mt test_regression
test-regression-erc20 :; eval QUOTE_PRECISION=${QUOTE_PRECISION} COLLATERAL_PRECISION=${COLLATERAL_PRECISION} BUCKET_INDEX_ERC20=${BUCKET_INDEX_ERC20} NO_OF_BUCKETS=${NO_OF_BUCKETS} forge t --mt test_regression --mc ERC20
test-regression-erc721 :; eval QUOTE_PRECISION=${QUOTE_PRECISION} BUCKET_INDEX_ERC721=${BUCKET_INDEX_ERC721} NO_OF_BUCKETS=${NO_OF_BUCKETS} forge t --mt test_regression --mc ERC721
coverage :; forge coverage --no-match-test "testLoad|invariant"
test-invariant-erc20-precision :; ./tests/forge/invariants/test-invariant-erc20-precision.sh
test-invariant-erc20-buckets :; ./tests/forge/invariants/test-invariant-erc20-buckets.sh
test-invariant-erc721-buckets :; ./tests/forge/invariants/test-invariant-erc721-buckets.sh
# Unit Tests
test :; forge test --no-match-test ${TEST_EXCLUDES} --nmc ${CONTRACT_EXCLUDES} # --ffi # enable if you need the `ffi` cheat code on HEVM
test-with-gas-report :; forge test --no-match-test ${TEST_EXCLUDES} --nmc ${CONTRACT_EXCLUDES} --gas-report # --ffi # enable if you need the `ffi` cheat code on HEVM

# Gas Load Tests
test-load :; forge test --match-test testLoad --gas-report

# Invariant Tests
test-invariant-all :; forge t --mt invariant --nmc ${CONTRACT_EXCLUDES}
test-invariant-erc20 :; forge t --mt invariant --nmc ${CONTRACT_EXCLUDES} --mc ERC20
test-invariant-erc721 :; forge t --mt invariant --nmc ${CONTRACT_EXCLUDES} --mc ERC721
test-invariant :; forge t --mt ${MT} --nmc RegressionTest
test-invariant-erc20-precision :; ./tests/forge/invariants/test-invariant-erc20-precision.sh
test-invariant-erc721-precision :; ./tests/forge/invariants/test-invariant-erc721-precision.sh
test-invariant-erc20-buckets :; ./tests/forge/invariants/test-invariant-erc20-buckets.sh
test-invariant-erc721-buckets :; ./tests/forge/invariants/test-invariant-erc721-buckets.sh

# Real-world simulation scenarios
test-rw-simulation-erc20 :; FOUNDRY_INVARIANT_SHRINK_SEQUENCE=false RUST_LOG=forge=info,foundry_evm=info,ethers=info forge t --mt invariant_all_erc20 --mc RealWorldScenario
test-rw-simulation-erc721 :; FOUNDRY_INVARIANT_SHRINK_SEQUENCE=false RUST_LOG=forge=info,foundry_evm=info,ethers=info forge t --mt invariant_all_erc721 --mc RealWorldScenario

# Liquidations load test scenarios
test-liquidations-load-erc20 :; FOUNDRY_INVARIANT_SHRINK_SEQUENCE=false RUST_LOG=forge=info,foundry_evm=info,ethers=info forge t --mt invariant_all_erc20 --mc PanicExitERC20
test-liquidations-load-erc721 :; FOUNDRY_INVARIANT_SHRINK_SEQUENCE=false RUST_LOG=forge=info,foundry_evm=info,ethers=info forge t --mt invariant_all_erc721 --mc PanicExitERC721

# Swap tokens load test scenarios
test-swap-load-erc20 :; FOUNDRY_INVARIANT_SHRINK_SEQUENCE=false RUST_LOG=forge=info,foundry_evm=info,ethers=info forge t --mt invariant_all_erc20 --mc TradingERC20

# Regression Tests
test-regression-all : test-regression-erc20 test-regression-erc721 test-regression-prototech
test-regression-erc20 :; forge t --mt test_regression --mc ERC20 --nmc "RealWorldRegression|Prototech"
test-regression-erc721 :; forge t --mt test_regression --mc ERC721 --nmc "RealWorldRegression|Prototech"
test-regression-prototech :; forge t --mt test_regression --mc Prototech
test-regression-rw :; forge t --mt test_regression --mc RealWorldRegression
test-regression :; forge t --mt ${MT}

# Coverage
coverage :; forge coverage --no-match-test "testLoad|invariant"

# Certora
certora-erc721-permit :; $(if $(CERTORAKEY),, @echo "set certora key"; exit 1;) PATH=~/.solc-select/artifacts/solc-0.8.14:~/.solc-select/artifacts:${PATH} certoraRun --solc_map PermitERC721Harness=solc-0.8.14,Auxiliar=solc-0.8.14,SignerMock=solc-0.8.14 --optimize_map PermitERC721Harness=500,Auxiliar=0,SignerMock=0 --rule_sanity basic certora/harness/PermitERC721Harness.sol certora/Auxiliar.sol certora/SignerMock.sol --verify PermitERC721Harness:certora/PermitERC721.spec --multi_assert_check $(if $(short), --short_output,)

# Generate Gas Snapshots
snapshot :; forge clean && forge snapshot
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ The Ajna protocol is a non-custodial, peer-to-peer, permissionless lending, borr
- The following types of tokens are incompatible with Ajna, and no countermeasures exist to explicitly prevent creating a pool with such tokens, actors should use them at their own risk:
- NFT and fungible tokens which charge a fee on transfer.
- Fungible tokens whose balance rebases.
- Fungible tokens with more than 18 decimals or 0 decimals.
- Fungible tokens with more than 18 decimals or 0 decimals, whose `decimals()` function does not return a constant value, or which do not implement the optional [decimals()](https://eips.ethereum.org/EIPS/eip-20#decimals) function.
- Borrowers cannot draw debt from a pool in the same block as when the pool was created.
- With the exception of quantized prices, pool inputs and most accumulators are not explicitly limited. The pool will stop functioning when the bounds of a `uint256` need to be exceeded to process a request.

Expand Down Expand Up @@ -130,9 +130,9 @@ bash ./check-code-coverage.sh
```bash
pip install slither-analyzer
```
- Make sure the default `solc` version is set to the same version as contracts (currently 0.8.14). This can be done by installing and using `solc-select`:
- Make sure the default `solc` version is set to the same version as contracts (currently 0.8.18). This can be done by installing and using `solc-select`:
```bash
pip install solc-select && solc-select install 0.8.14 && solc-select use 0.8.14
pip install solc-select && solc-select install 0.8.18 && solc-select use 0.8.18
```
- Run `analyze`

Expand Down
4 changes: 2 additions & 2 deletions brownie-config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ autofetch_sources: True
# path remapping to support imports from GitHub/NPM
compiler:
solc:
version: 0.8.14
version: 0.8.18
optimizer:
enabled: true
runs: 500
runs: 0
remappings:
- "@ds-math=lib/ds-math/src/"
- "@openzeppelin/contracts=lib/openzeppelin-contracts/contracts"
Expand Down
50 changes: 50 additions & 0 deletions certora/Auxiliar.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
pragma solidity 0.8.14;

contract Auxiliar {
function computeDigest(
bytes32 domain_separator,
bytes32 permit_typehash,
address spender,
uint256 tokenId,
uint256 nonce,
uint256 deadline
) public pure returns (bytes32 digest){
digest =
keccak256(
abi.encodePacked(
"\x19\x01",
domain_separator,
keccak256(
abi.encode(
permit_typehash,
spender,
tokenId,
nonce,
deadline
))
));
}

function call_ecrecover(
bytes32 digest,
uint8 v,
bytes32 r,
bytes32 s
) public pure returns (address signer) {
signer = ecrecover(digest, v, r, s);
}

function signatureToVRS(bytes memory signature) public returns (uint8 v, bytes32 r, bytes32 s) {
if (signature.length == 65) {
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
}
}

function isContract(address owner) public returns (bool) {
return owner.code.length > 0;
}
}
77 changes: 77 additions & 0 deletions certora/PermitERC721.spec
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// PermitERC721.spec

using Auxiliar as aux
using SignerMock as signer

methods {
getApproved(uint256) returns (address) envfree
ownerOf(uint256) returns (address) envfree
nonces(uint256) returns (uint96) envfree
DOMAIN_SEPARATOR() returns (bytes32) envfree
PERMIT_TYPEHASH() returns (bytes32) envfree
aux.call_ecrecover(bytes32, uint8, bytes32, bytes32) returns (address) envfree
aux.computeDigest(bytes32, bytes32, address, uint256, uint256, uint256) returns (bytes32) envfree
aux.signatureToVRS(bytes) returns (uint8, bytes32, bytes32) envfree
aux.isContract(address) returns (bool) envfree
isValidSignature(bytes32, bytes) returns (bytes4) => DISPATCHER(true)
}

// Verify that allowance behaves correctly on permit
rule permit(address spender, address tokenId, uint256 deadline, bytes signature) {
env e;

uint8 v; bytes32 r; bytes32 s;
v, r, s = aux.signatureToVRS(signature);

permit(e, spender, tokenId, deadline, v, r, s);

assert(getApproved(tokenId) == spender, "assert1 failed");
}

// Verify revert rules on permit
rule permit_revert(address spender, uint256 tokenId, uint256 deadline, bytes signature) {
env e;

uint8 v; bytes32 r; bytes32 s;
v, r, s = aux.signatureToVRS(signature);

uint256 tokenIdNonce = nonces(tokenId);
address owner = ownerOf(tokenId);

bytes32 digest = aux.computeDigest(
DOMAIN_SEPARATOR(),
PERMIT_TYPEHASH(),
spender,
tokenId,
tokenIdNonce,
deadline
);

address ownerRecover = aux.call_ecrecover(digest, v, r, s);
bytes32 returnedSig = signer.isValidSignature(e, digest, signature);
bool isContract = aux.isContract(owner);

permit@withrevert(e, spender, tokenId, deadline, v, r, s);

bool revert1 = e.msg.value > 0;
bool revert2 = e.block.timestamp > deadline;
bool revert3 = owner == 0;
bool revert4 = owner == spender;
bool revert5 = isContract && returnedSig != 0x1626ba7e00000000000000000000000000000000000000000000000000000000;
bool revert6 = !isContract && ownerRecover == 0;
bool revert7 = !isContract && ownerRecover != owner;
bool revert8 = tokenIdNonce == max_uint96;

assert(revert1 => lastReverted, "revert1 failed");
assert(revert2 => lastReverted, "revert2 failed");
assert(revert3 => lastReverted, "revert3 failed");
assert(revert4 => lastReverted, "revert4 failed");
assert(revert5 => lastReverted, "revert5 failed");
assert(revert6 => lastReverted, "revert6 failed");
assert(revert7 => lastReverted, "revert7 failed");
assert(revert8 => lastReverted, "revert8 failed");

assert(lastReverted => revert1 || revert2 || revert3 ||
revert4 || revert5 || revert6 ||
revert7 || revert8, "Revert rules are not covering all the cases");
}
11 changes: 11 additions & 0 deletions certora/SignerMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: AGPL-3.0-or-later

pragma solidity ^0.8.14;

contract SignerMock {
bytes32 sig;

function isValidSignature(bytes32, bytes memory) external view returns (bytes32) {
return sig;
}
}
18 changes: 18 additions & 0 deletions certora/harness/PermitERC721Harness.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
pragma solidity 0.8.14;

import { PermitERC721 } from '../../src/base/PermitERC721.sol';

contract PermitERC721Harness is PermitERC721 {

// overrides internal nonces
mapping(uint256 => uint96) public nonces;

constructor() PermitERC721("Ajna Positions NFT-V1", "AJNA-V1-POS", "1") public {}

// PostionManager.sol
function _getAndIncrementNonce(
uint256 tokenId_
) internal override returns (uint256) {
return uint256(nonces[tokenId_]++);
}
}
3 changes: 2 additions & 1 deletion docs/Functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,9 @@
- increment auctions count accumulator
- increment auctions.totalBondEscrowed accumulator
- updates auction queue state
- _updateKicker():
- _updateEscrowedBonds():
- update locked and claimable kicker accumulators
- update global escrow accumulator
- Loans.remove():
- delete borrower from indices => borrower address mapping
- remove loan from loans array
Expand Down
3 changes: 2 additions & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ block_number = 16_295_000
fork_block_number = 16_295_000
rpc_storage_caching = { chains = ["mainnet"], endpoints = "all" }
optimizer = true
optimizer_runs = 500
optimizer_runs = 0
fs_permissions = [{ access = "read-write", path = "./"}]

[fuzz]
runs = 300
Expand Down
2 changes: 1 addition & 1 deletion lib/forge-std
2 changes: 1 addition & 1 deletion script/deploy.s.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.14;
pragma solidity 0.8.18;

import { Script } from "forge-std/Script.sol";
import "forge-std/console.sol";
Expand Down
Loading

0 comments on commit b301603

Please sign in to comment.