-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3 from 0xJurassicPunk/tests-solidity
test: Solidity test helpers and simple tests
- Loading branch information
Showing
6 changed files
with
412 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
Oops, something went wrong.