Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: integration test #23

Merged
merged 3 commits into from
May 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions .github/workflows/integration-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
on: [ push, pull_request, workflow_dispatch ]

name: integration-test

jobs:
integration-test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [ 18 ]
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: yarn
- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
- id: forge_test
run: forge test -vvv --mc BaseIntegrationTest --fork-url https://mainnet.optimism.io
- name: income simulator
run: mkdir reports && forge test -vv --mp "test/integration/IncomeSimulator.t.sol" --fork-url https://mainnet.optimism.io
continue-on-error: true
- name: Archive Simulator Report
uses: actions/upload-artifact@v4
with:
name: income-simulator-report
path: |
reports/*.txt

24 changes: 24 additions & 0 deletions .github/workflows/invariant-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
on: [ workflow_dispatch ]

name: invariant-test

jobs:
invariant-test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [ 18 ]
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: yarn
- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
- id: forge_test
run: forge test -vvv --mc BaseInvariantTest --fork-url https://optimism.llamarpc.com/sk_llama_115e7405eff4c29287d6ff9a0275bf84
continue-on-error: true

4 changes: 2 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ jobs:
- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
- name: Cache fork requests
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: ~/.foundry/cache
key: "${{ runner.os }}-foundry-network-fork-${{ github.sha }}"
restore-keys: |
${{ runner.os }}-foundry-network-fork-
- id: forge_test
run: forge test --fork-url https://mainnet.optimism.io
run: forge test -vvv --mp "test/unit/*.sol" --fork-url https://mainnet.optimism.io
continue-on-error: true
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,7 @@ node_modules
.vscode
.DS_Store
coverage.json
lcov.info
lcov.info

report.txt
reports
4 changes: 3 additions & 1 deletion contracts/lib/BondingCurveLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ library BondingCurveLib {
uint256 h = inflectionPrice;

// Early return to save gas if either `g` or `h` is zero.
if (g * h == 0) return 0;
aquariuslt marked this conversation as resolved.
Show resolved Hide resolved
if (g * h == 0) {
return 0;
}

uint256 s = uint256(fromSupply) + 1;
uint256 end = s + uint256(quantity);
Expand Down
13 changes: 12 additions & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ out = 'out'
libs = ['node_modules', 'lib']
test = 'test'
cache_path = 'cache-foundry'
fs_permissions = [{ access = "write", path = "./"}]

[fmt]
line_length = 120 # Maximum line length where formatter will try to wrap the line
Expand All @@ -19,4 +20,14 @@ quote_style = 'double'
[fuzz]
runs = 10

# See more config options https://github.com/gakonst/foundry/tree/master/config
[invariant]
runs = 100
depth = 1
verbosity = 4
fail_on_revert = false
call_override = false
dictionary_weight = 80
include_storage = true
include_push_bytes = true

# See more config options https://github.com/foundry-rs/foundry/tree/master/crates/config
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@
"scripts": {
"build": "forge build",
"compile": "forge compile",
"test": "forge test --fork-url https://mainnet.optimism.io",
"coverage": "forge coverage --fork-url https://mainnet.optimism.io",
"test": "forge test -vvv --mp \"test/unit/**/*.sol\" --fork-url https://mainnet.optimism.io",
"test:integration": "forge test -vvv --mp \"test/integration/*/*.sol\" --fork-url https://mainnet.optimism.io ",
"test:invariant": "forge test -vvv --mc BaseInvariantTest --fork-url https://optimism.llamarpc.com/sk_llama_3f92d666a172604faf69e469a67ec6ea ",
"test:income": "forge test -vvvv --mp \"test/integration/IncomeSimulator.t.sol\" --fork-url https://mainnet.optimism.io ",
"coverage": "forge coverage --mp \"test/unit/**/*.sol\" --fork-url https://mainnet.optimism.io ",
"deploy:testnet": "make deploy-testnet",
"deploy:mainnet": "make deploy-mainnet",
"fmt": "forge fmt --check && solhint \"{scripts,contracts,test}/**/*.sol\"",
Expand Down
2 changes: 1 addition & 1 deletion remappings.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ contracts/=contracts/
@openzeppelin/contracts=node_modules/@openzeppelin/contracts
solady/=node_modules/solady/src/
forge-std/=node_modules/forge-std/src/
ds-test/=node_modules/ds-test/src
ds-test/src/=node_modules/ds-test/src/
175 changes: 175 additions & 0 deletions test/integration/BaseIntegrationTest.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
pragma solidity ^0.8.25;

import { Test } from "forge-std/Test.sol";
aquariuslt marked this conversation as resolved.
Show resolved Hide resolved
import { console } from "forge-std/console.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SharesFactoryV1 } from "contracts/core/SharesFactoryV1.sol";
import { SharesERC1155 } from "contracts/core/SharesERC1155.sol";
import { AaveYieldAggregator } from "contracts/core/aggregator/AaveYieldAggregator.sol";
import { BlankYieldAggregator } from "contracts/core/aggregator/BlankYieldAggregator.sol";
import { IAavePool } from "contracts/interface/IAave.sol";

contract BaseIntegrationTest is Test {
address public SHARES_FOUNDER = makeAddr("founder");
address public FACTORY_OWNER = makeAddr("factoryOwner");

// spam roles
address public SPAM_BOT = makeAddr("spamBot");
address public HACKER = makeAddr("hacker");

// normal traders
address public DAILY_TRADER = makeAddr("dailyTrader");
address public LONG_TERM_TRADER = makeAddr("longTermTrader");

struct PresetCurveParams {
uint96 basePrice;
uint32 inflectionPoint;
uint128 inflectionPrice;
uint128 linearPriceSlope;
}

SharesERC1155 public sharesNFT;
SharesFactoryV1 public sharesFactory;

AaveYieldAggregator public aaveYieldAggregator;
BlankYieldAggregator public blankYieldAggregator;

address public constant WETH = 0x4200000000000000000000000000000000000006;
address public constant AAVE_POOL = 0x794a61358D6845594F94dc1DB02A252b5b4814aD;
address public constant AAVE_WETH_GATEWAY = 0xe9E52021f4e11DEAD8661812A0A6c8627abA2a54;

IERC20 public aWETH = IERC20(IAavePool(AAVE_POOL).getReserveData(WETH).aTokenAddress);

string public constant BASE_URI = "https://foo.com/shares/uri/";

PresetCurveParams public DEFAULT_CURVE_PARAMS = PresetCurveParams({
basePrice: 0.005 ether,
inflectionPoint: 1500,
inflectionPrice: 0.01025 ether,
linearPriceSlope: 0
});

PresetCurveParams public STANDARD_CURVE_PARAMS = PresetCurveParams({
basePrice: 0.001 ether,
inflectionPoint: 1500,
inflectionPrice: 0.002 ether,
linearPriceSlope: 0
});

PresetCurveParams public EXCLUSIVE_CURVE_PARAMS = PresetCurveParams({
basePrice: 0.01 ether,
inflectionPoint: 1500,
inflectionPrice: 0.02 ether,
linearPriceSlope: 0
});

uint8 public constant DEFAULT_CURVE_TYPE = 0;
uint8 public constant STANDARD_CURVE_TYPE = 1;
uint8 public constant EXCLUSIVE_CURVE_TYPE = 2;

// common deploy contracts with args, call if needed
function deployContracts() public {
vm.startPrank(FACTORY_OWNER);
sharesNFT = new SharesERC1155(BASE_URI);

sharesFactory = new SharesFactoryV1(
address(sharesNFT),
DEFAULT_CURVE_PARAMS.basePrice,
DEFAULT_CURVE_PARAMS.inflectionPoint,
DEFAULT_CURVE_PARAMS.inflectionPrice,
DEFAULT_CURVE_PARAMS.linearPriceSlope
);

aaveYieldAggregator = new AaveYieldAggregator(address(sharesFactory), WETH, AAVE_POOL, AAVE_WETH_GATEWAY);
blankYieldAggregator = new BlankYieldAggregator(address(sharesFactory), WETH);


sharesNFT.setFactory(address(sharesFactory));
sharesFactory.resetYield(address(blankYieldAggregator));

sharesNFT.transferOwnership(FACTORY_OWNER);
aaveYieldAggregator.transferOwnership(FACTORY_OWNER);

sharesFactory.transferOwnership(FACTORY_OWNER);
sharesFactory.acceptOwnership();

sharesFactory.queueMigrateYield(address(aaveYieldAggregator));
vm.warp(block.timestamp + 4 days);
sharesFactory.executeMigrateYield();

vm.stopPrank();
}

// common set curve functions, call if need
function createPresetCurveTypes() public {
vm.startPrank(FACTORY_OWNER);
sharesFactory.setCurveType(
STANDARD_CURVE_TYPE,
STANDARD_CURVE_PARAMS.basePrice,
STANDARD_CURVE_PARAMS.inflectionPoint,
STANDARD_CURVE_PARAMS.inflectionPrice,
STANDARD_CURVE_PARAMS.linearPriceSlope
);

sharesFactory.setCurveType(
EXCLUSIVE_CURVE_TYPE,
EXCLUSIVE_CURVE_PARAMS.basePrice,
EXCLUSIVE_CURVE_PARAMS.inflectionPoint,
EXCLUSIVE_CURVE_PARAMS.inflectionPrice,
EXCLUSIVE_CURVE_PARAMS.linearPriceSlope
);
vm.stopPrank();
}

// create buy histories as fixtures
function createPresetBuyHistory(uint8 _tradeCount) public {
uint8 tradeCount = 0;
if (_tradeCount > 0) {
tradeCount = _tradeCount;
}

vm.deal(DAILY_TRADER, 100 ether);
vm.deal(LONG_TERM_TRADER, 100 ether);

vm.prank(DAILY_TRADER);
sharesFactory.mintShare(DEFAULT_CURVE_TYPE);
uint256 shareId = sharesFactory.shareIndex() - 1;

for (uint32 i = 0; i < tradeCount; i++) {
_buyShare(DAILY_TRADER, shareId, 1, SHARES_FOUNDER);
}

uint256 currentBlockTime = block.timestamp;
vm.warp(currentBlockTime + 10 minutes);

for (uint32 i = 0; i < tradeCount; i++) {
_buyShare(LONG_TERM_TRADER, shareId, 1, SHARES_FOUNDER);
}

console.log("[DEBUG]: After buy times %s, DAILY_TRADER balance", tradeCount, DAILY_TRADER.balance);
console.log("[DEBUG]: After buy times %s, DAILY_TRADER balance", tradeCount, LONG_TERM_TRADER.balance);
}

// common initial contract with specific args
// @dev call after deployContracts and if needed

function _buyShare(address sender, uint256 shareId, uint32 quantity, address referral) internal {
(uint256 buyPriceAfterFee,,,) = sharesFactory.getBuyPriceAfterFee(shareId, quantity, referral);

vm.prank(address(sender));
sharesFactory.buyShare{ value: buyPriceAfterFee }(shareId, quantity, referral);
}

function _sellShare(address sender, uint256 shareId, uint32 quantity, address referral) internal {
(uint256 sellPriceAfterFee,,,) = sharesFactory.getSellPriceAfterFee(shareId, quantity, referral);

vm.prank(address(sender));
sharesFactory.sellShare(shareId, quantity, sellPriceAfterFee, referral);
}

function test_success() public { }

function _logToFile(string memory _path, string memory _messages) internal {
vm.writeLine(_path, _messages);
}
}
Loading
Loading