Skip to content

Commit

Permalink
Merge pull request #3 from 0xJurassicPunk/tests-solidity
Browse files Browse the repository at this point in the history
test: Solidity test helpers and simple tests
  • Loading branch information
0xJurassicPunk committed Mar 21, 2022
2 parents b525d97 + a0c7811 commit 1562ce6
Show file tree
Hide file tree
Showing 6 changed files with 412 additions and 0 deletions.
165 changes: 165 additions & 0 deletions contracts/test/AggregatorFeeSharingWithUniswapV3.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.7;

import {DSTest} from "../../lib/ds-test/src/test.sol";

import {LooksRareToken} from "../LooksRareToken.sol";
import {TokenDistributor} from "../TokenDistributor.sol";
import {FeeSharingSystem} from "../FeeSharingSystem.sol";
import {AggregatorFeeSharingWithUniswapV3} from "../AggregatorFeeSharingWithUniswapV3.sol";
import {MockERC20} from "./utils/MockERC20.sol";
import {MockUniswapV3Router} from "./utils/MockUniswapV3Router.sol";

import {TestHelpers} from "./TestHelpers.sol";
import {ICheatCodes} from "./ICheatCodes.sol";

abstract contract TestParameters {
address internal _PREMINT_RECEIVER = address(42);
address internal _TOKEN_SPLITTER = address(88);
uint256 internal _CAP = 25000;
uint256 internal _PREMINT_AMOUNT = 6250;
uint256 internal _START_BLOCK;
}

contract AggregatorTest is DSTest, TestParameters, TestHelpers {
ICheatCodes public cheats = ICheatCodes(HEVM_ADDRESS);

LooksRareToken public looksRareToken;
TokenDistributor public tokenDistributor;
FeeSharingSystem public feeSharingSystem;
AggregatorFeeSharingWithUniswapV3 public aggregatorFeeSharingWithUniswapV3;
MockUniswapV3Router public uniswapRouter;
MockERC20 public rewardToken;

function setUp() public {
// 0. Mock WETH
rewardToken = new MockERC20("WETH", "Wrapped Ether");

// 1. Mock Uniswap v3 Router
uniswapRouter = new MockUniswapV3Router();

// 2. LooksRareToken deployment
looksRareToken = new LooksRareToken(
_PREMINT_RECEIVER,
_parseEther(_PREMINT_AMOUNT),
_parseEther(_CAP)
);

// 3. TokenDistributor deployment
uint256[] memory rewardsPerBlockForStaking = new uint256[](4);
rewardsPerBlockForStaking[0] = _parseEther(30);
rewardsPerBlockForStaking[1] = _parseEther(15);
rewardsPerBlockForStaking[2] = _parseEtherWithFloating(75, 1); // 7.5
rewardsPerBlockForStaking[3] = _parseEtherWithFloating(375, 2); // 3.75

uint256[] memory rewardsPerBlockForOthers = new uint256[](4);
rewardsPerBlockForOthers[0] = _parseEther(70);
rewardsPerBlockForOthers[1] = _parseEther(35);
rewardsPerBlockForOthers[2] = _parseEtherWithFloating(175, 1); // 17.5
rewardsPerBlockForOthers[3] = _parseEtherWithFloating(875, 2); // 8.75

uint256[] memory periodLengthesInBlocks = new uint256[](4);
periodLengthesInBlocks[0] = uint256(100);
periodLengthesInBlocks[1] = uint256(100);
periodLengthesInBlocks[2] = uint256(100);
periodLengthesInBlocks[3] = uint256(100);

_START_BLOCK = block.number + 10;

// 4. TokenDistributor deployment
tokenDistributor = new TokenDistributor(
address(looksRareToken),
_TOKEN_SPLITTER,
_START_BLOCK,
rewardsPerBlockForStaking,
rewardsPerBlockForOthers,
periodLengthesInBlocks,
4
);

looksRareToken.transferOwnership(address(tokenDistributor));

// 5. FeeSharingSystem deployment
feeSharingSystem = new FeeSharingSystem(
address(looksRareToken),
address(rewardToken),
address(tokenDistributor)
);

// 6. Aggregator deployment
aggregatorFeeSharingWithUniswapV3 = new AggregatorFeeSharingWithUniswapV3(
address(feeSharingSystem),
address(uniswapRouter)
);

aggregatorFeeSharingWithUniswapV3.startHarvest();
aggregatorFeeSharingWithUniswapV3.updateThresholdAmount(
_parseEtherWithFloating(5, 1)
);
aggregatorFeeSharingWithUniswapV3.updateHarvestBufferBlocks(10);

// 7. Distribute LOOKS to user accounts (from the premint)
address[4] memory users = [
address(1),
address(2),
address(3),
address(4)
];

for (uint256 i = 0; i < users.length; i++) {
cheats.prank(_PREMINT_RECEIVER);
looksRareToken.transfer(users[i], _parseEther(200));

cheats.prank(users[i]);
looksRareToken.approve(
address(aggregatorFeeSharingWithUniswapV3),
type(uint256).max
);
}
}

function testConstructor() public {
assertEq(looksRareToken.name(), "LooksRare Token");
assertEq(looksRareToken.symbol(), "LOOKS");
assertEq(looksRareToken.totalSupply(), _parseEther(_PREMINT_AMOUNT));
assertEq(_parseEther(1), _parseEtherWithFloating(1, 0));
}

function testDeposit() public {
address user1 = address(1);

cheats.startPrank(user1);
aggregatorFeeSharingWithUniswapV3.deposit(_parseEther(100));
assertEq(
aggregatorFeeSharingWithUniswapV3.userInfo(user1),
_parseEther(100)
);
assertEq(
aggregatorFeeSharingWithUniswapV3.calculateSharesValueInLOOKS(
user1
),
_parseEther(100)
);

cheats.roll(_START_BLOCK + 1);

assertEq(
aggregatorFeeSharingWithUniswapV3.calculateSharesValueInLOOKS(
user1
),
_parseEther(130)
);

aggregatorFeeSharingWithUniswapV3.withdrawAll();

// 100 LOOKS + 130 LOOKS = 230 LOOKS
assertEq(looksRareToken.balanceOf(user1), _parseEther(230));

assertEq(
aggregatorFeeSharingWithUniswapV3.calculateSharesValueInLOOKS(
user1
),
_parseEther(0)
);
}
}
102 changes: 102 additions & 0 deletions contracts/test/ICheatCodes.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface ICheatCodes {
// Set block.timestamp (newTimestamp)
function warp(uint256) external;

// Set block.height (newHeight)
function roll(uint256) external;

// Set block.basefee (newBasefee)
function fee(uint256) external;

// Loads a storage slot from an address (who, slot)
function load(address, bytes32) external returns (bytes32);

// Stores a value to an address' storage slot, (who, slot, value)
function store(
address,
bytes32,
bytes32
) external;

// Signs data, (privateKey, digest) => (v, r, s)
function sign(uint256, bytes32)
external
returns (
uint8,
bytes32,
bytes32
);

// Gets address for a given private key, (privateKey) => (address)
function addr(uint256) external returns (address);

// Performs a foreign function call via terminal, (stringInputs) => (result)
function ffi(string[] calldata) external returns (bytes memory);

// Sets the *next* call's msg.sender to be the input address
function prank(address) external;

// Sets all subsequent calls' msg.sender to be the input address until `stopPrank` is called
function startPrank(address) external;

// Sets the *next* call's msg.sender to be the input address, and the tx.origin to be the second input
function prank(address, address) external;

// Sets all subsequent calls' msg.sender to be the input address until `stopPrank` is called, and the tx.origin to be the second input
function startPrank(address, address) external;

// Resets subsequent calls' msg.sender to be `address(this)`
function stopPrank() external;

// Sets an address' balance, (who, newBalance)
function deal(address, uint256) external;

// Sets an address' code, (who, newCode)
function etch(address, bytes calldata) external;

// Expects an error on next call
function expectRevert(bytes calldata) external;

function expectRevert(bytes4) external;

// Record all storage reads and writes
function record() external;

// Gets all accessed reads and write slot from a recording session, for a given address
function accesses(address)
external
returns (bytes32[] memory reads, bytes32[] memory writes);

// Prepare an expected log with (bool checkTopic1, bool checkTopic2, bool checkTopic3, bool checkData).
// Call this function, then emit an event, then call a function. Internally after the call, we check if
// logs were emitted in the expected order with the expected topics and data (as specified by the booleans)
function expectEmit(
bool,
bool,
bool,
bool
) external;

// Mocks a call to an address, returning specified data.
// Calldata can either be strict or a partial match, e.g. if you only
// pass a Solidity selector to the expected calldata, then the entire Solidity
// function will be mocked.
function mockCall(
address,
bytes calldata,
bytes calldata
) external;

// Clears all mocked calls
function clearMockedCalls() external;

// Expect a call to an address with the specified calldata.
// Calldata can either be strict or a partial match
function expectCall(address, bytes calldata) external;

// Fetches the contract bytecode from its artifact file
function getCode(string calldata) external returns (bytes memory);
}
18 changes: 18 additions & 0 deletions contracts/test/TestHelpers.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// SPDX-License-Identifier: MIT
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

abstract contract TestHelpers {
function _parseEther(uint256 value) internal pure returns (uint256) {
return value * 1e18;
}

function _parseEtherWithFloating(uint256 value, uint8 floatingDigits)
internal
pure
returns (uint256)
{
assert(floatingDigits <= 18);
return value * (10**(18 - floatingDigits));
}
}
14 changes: 14 additions & 0 deletions contracts/test/utils/MockERC20.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract MockERC20 is ERC20 {
constructor(string memory _name, string memory _symbol) ERC20(_name, _symbol) {
//
}

function mint(address to, uint256 amount) external {
_mint(to, amount);
}
}
36 changes: 36 additions & 0 deletions contracts/test/utils/MockRewardConvertor.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

import "../../interfaces/IRewardConvertor.sol";

contract MockRewardConvertor is IRewardConvertor {
address public immutable FEE_SHARING_ADDRESS;

constructor(address _feeSharingAddress) {
FEE_SHARING_ADDRESS = _feeSharingAddress;
}

function convert(
address tokenToSell,
address tokenToBuy,
uint256 amount,
bytes calldata
) external override returns (uint256) {
require(
msg.sender == FEE_SHARING_ADDRESS,
"Convert: Not the fee sharing"
);

uint256 amountToTransfer = IERC20(tokenToBuy).balanceOf(address(this));

// Transfer from
IERC20(tokenToSell).transferFrom(msg.sender, address(this), amount);

// Transfer to
IERC20(tokenToBuy).transfer(FEE_SHARING_ADDRESS, amountToTransfer);

return amountToTransfer;
}
}
Loading

0 comments on commit 1562ce6

Please sign in to comment.