Skip to content

Commit

Permalink
test: add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Jean-Grimal committed Oct 15, 2024
1 parent 59e8249 commit 4a5125b
Show file tree
Hide file tree
Showing 5 changed files with 241 additions and 4 deletions.
7 changes: 7 additions & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,11 @@ src = "src"
out = "out"
libs = ["lib"]

[profile.default.fuzz]
runs = 16


[profile.default.rpc_endpoints]
ethereum = "https://eth-mainnet.g.alchemy.com/v2/${ALCHEMY_KEY}"

# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options
137 changes: 133 additions & 4 deletions test/MorphoToken.t.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {Test, console} from "forge-std/Test.sol";
import {BaseTest} from "./helpers/BaseTest.sol";
import {SigUtils} from "./helpers/SigUtils.sol";
import {MorphoToken} from "../src/MorphoToken.sol";

// TODO: Test the following:
Expand All @@ -11,8 +12,136 @@ import {MorphoToken} from "../src/MorphoToken.sol";
// - Test access control
// - Test voting
// - Test delegation
contract MorphoTokenTest is Test {
MorphoToken public token;
contract MorphoTokenTest is BaseTest {
function testUpgradeNotOwner(address updater) public {
vm.assume(updater != address(0));
vm.assume(updater != MORPHO_DAO);

function setUp() public {}
address newImplem = address(new MorphoToken());

vm.expectRevert();
newMorpho.upgradeToAndCall(newImplem, hex"");
}

function testUpgrade() public {
address newImplem = address(new MorphoToken());

vm.prank(MORPHO_DAO);
newMorpho.upgradeToAndCall(newImplem, hex"");
}

function testOwnDelegation(address delegator, uint256 amount) public {
vm.assume(delegator != address(0));
vm.assume(delegator != MORPHO_DAO);
amount = bound(amount, MIN_TEST_AMOUNT, MAX_TEST_AMOUNT);

deal(address(newMorpho), delegator, amount);

vm.prank(delegator);
newMorpho.delegate(delegator);

assertEq(newMorpho.delegates(delegator), delegator);
assertEq(newMorpho.getVotes(delegator), amount);
}

function testDelegate(address delegator, address delegatee, uint256 amount) public {
address[] memory addresses = new address[](2);
addresses[0] = delegator;
addresses[1] = delegatee;
_validateAddresses(addresses);
amount = bound(amount, MIN_TEST_AMOUNT, MAX_TEST_AMOUNT);

deal(address(newMorpho), delegator, amount);

vm.prank(delegator);
newMorpho.delegate(delegatee);

assertEq(newMorpho.delegates(delegator), delegatee);
assertEq(newMorpho.getVotes(delegator), 0);
assertEq(newMorpho.getVotes(delegatee), amount);
}

function testDelegateBySig(SigUtils.Delegation memory delegation, uint256 privateKey, uint256 amount) public {
privateKey = bound(privateKey, 1, type(uint32).max);
address delegator = vm.addr(privateKey);

address[] memory addresses = new address[](2);
addresses[0] = delegator;
addresses[1] = delegation.delegatee;
_validateAddresses(addresses);

delegation.expiry = bound(delegation.expiry, block.timestamp, type(uint256).max);
delegation.nonce = 0;

amount = bound(amount, MIN_TEST_AMOUNT, MAX_TEST_AMOUNT);
deal(address(newMorpho), delegator, amount);

Signature memory sig;
bytes32 digest = SigUtils.getTypedDataHash(delegation);
(sig.v, sig.r, sig.s) = vm.sign(privateKey, digest);

newMorpho.delegateBySig(delegation.delegatee, delegation.nonce, delegation.expiry, sig.v, sig.r, sig.s);

assertEq(newMorpho.delegates(delegator), delegation.delegatee);
assertEq(newMorpho.getVotes(delegator), 0);
assertEq(newMorpho.getVotes(delegation.delegatee), amount);
}

function testMultipleDelegation(
address delegator1,
address delegator2,
address delegatee,
uint256 amount1,
uint256 amount2
) public {
address[] memory addresses = new address[](3);
addresses[0] = delegator1;
addresses[1] = delegator2;
addresses[2] = delegatee;
_validateAddresses(addresses);
amount1 = bound(amount1, MIN_TEST_AMOUNT, MAX_TEST_AMOUNT);
amount2 = bound(amount2, MIN_TEST_AMOUNT, MAX_TEST_AMOUNT);

deal(address(newMorpho), delegator1, amount1);
deal(address(newMorpho), delegator2, amount2);

vm.prank(delegator1);
newMorpho.delegate(delegatee);

vm.prank(delegator2);
newMorpho.delegate(delegatee);

assertEq(newMorpho.getVotes(delegatee), amount1 + amount2);
}

function testTransferVotingPower(
address delegator1,
address delegator2,
address delegatee1,
address delegatee2,
uint256 initialAmount,
uint256 transferedAmount
) public {
address[] memory addresses = new address[](4);
addresses[0] = delegator1;
addresses[1] = delegator2;
addresses[2] = delegatee1;
addresses[3] = delegatee2;
_validateAddresses(addresses);
initialAmount = bound(initialAmount, MIN_TEST_AMOUNT, MAX_TEST_AMOUNT);
transferedAmount = bound(transferedAmount, MIN_TEST_AMOUNT, initialAmount);

deal(address(newMorpho), delegator1, initialAmount);

vm.prank(delegator2);
newMorpho.delegate(delegatee2);

vm.startPrank(delegator1);
newMorpho.delegate(delegatee1);
newMorpho.transfer(delegator2, transferedAmount);
vm.stopPrank();

assertEq(newMorpho.getVotes(delegatee1), initialAmount - transferedAmount);
assertEq(newMorpho.getVotes(delegatee2), transferedAmount);
}
}
25 changes: 25 additions & 0 deletions test/MorphoTokenMigration.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {BaseTest} from "./helpers/BaseTest.sol";

// TODO: Test the following:
// - Test migration flow
// - Test bundler wrapping
contract MorphoTokenMigrationTest is BaseTest {
uint256 internal forkId;

function setUp() public virtual override {
// DEPLOYMENTS
_fork();
super.setUp();
}

function _fork() internal virtual {
string memory rpcUrl = vm.rpcUrl("ethereum");
uint256 forkBlockNumber = 20969715;

forkId = vm.createSelectFork(rpcUrl, forkBlockNumber);
vm.chainId(1);
}
}
54 changes: 54 additions & 0 deletions test/helpers/BaseTest.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {Test, console} from "lib/forge-std/src/Test.sol";
import {MorphoToken} from "../../src/MorphoToken.sol";
import {Wrapper} from "../../src/Wrapper.sol";
import {ERC1967Proxy} from
"lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import {UUPSUpgradeable} from "lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol";

// TODO: Test the following:
// - Test every paths
// - Test migration flow
// - Test bundler wrapping
// - Test access control
// - Test voting
// - Test delegation
contract BaseTest is Test {
address public constant MORPHO_DAO = 0xcBa28b38103307Ec8dA98377ffF9816C164f9AFa;

MorphoToken public tokenImplem;
MorphoToken public newMorpho;
ERC1967Proxy public tokenProxy;
Wrapper public wrapper;

uint256 internal constant MIN_TEST_AMOUNT = 100;
uint256 internal constant MAX_TEST_AMOUNT = 1e28;

function setUp() public virtual {
// DEPLOYMENTS
tokenImplem = new MorphoToken();
tokenProxy = new ERC1967Proxy(address(tokenImplem), hex"");
wrapper = new Wrapper(address(tokenProxy));

newMorpho = MorphoToken(payable(address(tokenProxy)));
newMorpho.initialize(MORPHO_DAO, address(wrapper));
}

function _validateAddresses(address[] memory addresses) internal pure {
for (uint256 i = 0; i < addresses.length; i++) {
vm.assume(addresses[i] != address(0));
vm.assume(addresses[i] != MORPHO_DAO);
for (uint256 j = i + 1; j < addresses.length; j++) {
vm.assume(addresses[i] != addresses[j]);
}
}
}

struct Signature {
uint8 v;
bytes32 r;
bytes32 s;
}
}
22 changes: 22 additions & 0 deletions test/helpers/SigUtils.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

library SigUtils {
struct Delegation {
address delegatee;
uint256 nonce;
uint256 expiry;
}

bytes32 private constant DELEGATION_TYPEHASH =
keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)");

/// @dev Computes the hash of the EIP-712 encoded data.
function getTypedDataHash(Delegation memory delegation) public pure returns (bytes32) {
return keccak256(bytes.concat("\x19\x01", hashStruct(delegation)));
}

function hashStruct(Delegation memory delegation) internal pure returns (bytes32) {
return keccak256(abi.encode(DELEGATION_TYPEHASH, delegation.delegatee, delegation.nonce, delegation.expiry));
}
}

0 comments on commit 4a5125b

Please sign in to comment.