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

DONT MERGE Phase2 #31

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
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
228 changes: 165 additions & 63 deletions .gas-snapshot

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ A set of smart contracts tools for governance written in vyper
## Contracts

* Timelock.vy - "Vyper implementation of a timelock contract for governance"
* DualTimelock.vy - "Timelock that can work with two queues with different delay settings"
* SerpentorBravo.vy - "Vyper implementation of a governance contract for on-chain voting on proposals and execution"
* LeanTrack.vy - "Implementation for Optimistic on-chain governance system of motions to govern smart contracts"

## Requirements

Expand Down Expand Up @@ -76,6 +78,10 @@ ape compile
ape test
```

## Compatibility
This project aims to be compatible with most governance contracts and best practices.
This implementation is mainly designed to work with any token implementing COMP token voting weight functions like `getPriorVotes`, but in most cases minimal changes are required to interact with other smart contracts implementations like OZ voting tokens.

## Disclaimer

This is **experimental software** and is provided on an "as is" and "as available" basis **without any warranties**.
Expand All @@ -91,5 +97,6 @@ Use at your own risk.
## Acknowledgements

- [compound governance](https://github.com/compound-finance/compound-protocol/tree/master/contracts/Governance)
- [Easy Track](https://github.com/lidofinance/easy-track)
- [snekmate](https://github.com/pcaversaccio/snekmate)
- [vyperDeployer](https://github.com/0xKitsune/Foundry-Vyper/blob/main/lib/utils/VyperDeployer.sol)
2 changes: 1 addition & 1 deletion ape-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ plugins:
- name: infura
- name: tokens

# require OpenZepplin Contracts
# require OpenZeppelin Contracts
dependencies:
- name: openzeppelin
github: OpenZeppelin/openzeppelin-contracts
Expand Down
163 changes: 163 additions & 0 deletions foundry_test/BaseMotionFactory.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
// SPDX-License-Identifier: AGPL
pragma solidity ^0.8.16;

import "@openzeppelin/token/ERC20/ERC20.sol";
import {console} from "forge-std/console.sol";

import {ExtendedTest} from "./utils/ExtendedTest.sol";
import {VyperDeployer} from "../lib/utils/VyperDeployer.sol";
import {DualTimelock} from "./interfaces/DualTimelock.sol";
import {
LeanTrack,
Factory,
Motion
} from "./interfaces/LeanTrack.sol";
import {GovToken} from "./utils/GovToken.sol";
import {TransferMotionFactory} from "../src/factories/examples/TransferMotionFactory.sol";

import {MockLeanTrack, MotionArgs} from "./utils/MockLeanTrack.sol";

// these tests covers both the example TransferMotionFactory and the BaseMotionFactory contracts
contract BaseMotionFactoryTest is ExtendedTest {
uint256 public constant DEFAULT_LIMIT = 1000;
VyperDeployer private vyperDeployer = new VyperDeployer();
DualTimelock private timelock;
ERC20 private token;
TransferMotionFactory private transferfactory;
MockLeanTrack private leanTrack;
GovToken private govToken;

uint256 public delay = 2 days;
uint256 public leanTrackDelay = 1 days;
uint256 public factoryMotionDuration = 1 days;

address public admin = address(1);
address public authorized = address(2);
address public objectoor = address(3);
address public mediumVoter = address(4);
address public whaleVoter1 = address(5);
address public whaleVoter2 = address(6);
address public knight = address(7);
address public smallVoter = address(8);
address public grantee = address(0xABCD);
address public executor = address(7);

event MotionCreated(
address[] targets,
uint256[] values,
string[] signatures,
bytes[] calldatas
);

function setUp() public {
// deploy token
govToken = new GovToken(18);
token = ERC20(govToken);

// deploy mock lean track
leanTrack = new MockLeanTrack();

transferfactory = new TransferMotionFactory(address(leanTrack), address(admin));

// set transfer limit
hoax(admin);
transferfactory.setTransferLimit(address(token), DEFAULT_LIMIT);
// set authorized transfer motion creator
hoax(admin);
transferfactory.setAuthorized(authorized, true);

// vm traces
vm.label(address(transferfactory), "transferfactory");
vm.label(address(leanTrack), "leanTrack");
vm.label(address(govToken), "govToken");
vm.label(address(token), "token");
}
function testSetup() public {
assertEq(address(transferfactory.gov()), admin);
assertEq(address(transferfactory.leanTrack()), address(leanTrack));

assertEq(transferfactory.transferLimits(address(token)), DEFAULT_LIMIT);
assertEq(transferfactory.authorized(authorized), true);
}

function testCreateTransferMotion() public {
// create transfer motion
hoax(authorized);
uint256 motionId = transferfactory.createTransferMotion(address(token), grantee, 100);

assertEq(motionId, 1);
address[] memory targets = new address[](1);
targets[0] = address(token);
uint256[] memory values = new uint256[](1);
values[0] = 0;
string[] memory signatures = new string[](1);
signatures[0] = "transfer(address,uint256)";
bytes[] memory calldatas = new bytes[](1);
calldatas[0] = abi.encode(grantee, 100);

// check lean track was called with expected params
MotionArgs memory motionArgs = leanTrack.getMotionArgs(motionId);
assertEq(motionArgs.id, motionId);
assertEq(motionArgs.targets, targets);
assertEq(motionArgs.calldatas[0], calldatas[0]);
assertEq(motionArgs.signatures[0], signatures[0]);
assertEq(motionArgs.values[0], values[0]);
}

function testCannotCreateMotionWithZeroAmount() public {
vm.expectRevert("!amount");

// create transfer motion
hoax(authorized);
transferfactory.createTransferMotion(address(token), grantee, 0);
}

function testOnlyAuthorizedCanCreateMotion() public {
vm.expectRevert("!auth");

// create transfer motion
hoax(objectoor);
transferfactory.createTransferMotion(address(token), grantee, 100);
}

function testRandomCanotCallSetAuthorized() public {
vm.expectRevert();

// set authorized
hoax(objectoor);
transferfactory.setAuthorized(objectoor, true);
}


function testCancelTransferMotion() public {
// create transfer motion
hoax(authorized);
uint256 motionId = transferfactory.createTransferMotion(address(token), grantee, 100);

assertEq(motionId, 1);
address[] memory targets = new address[](1);
targets[0] = address(token);
uint256[] memory values = new uint256[](1);
values[0] = 0;
string[] memory signatures = new string[](1);
signatures[0] = "transfer(address,uint256)";
bytes[] memory calldatas = new bytes[](1);
calldatas[0] = abi.encode(grantee, 100);

// check lean track was called with expected params
assertEq(leanTrack.motions(motionId).id, motionId);
MotionArgs memory motionArgs = leanTrack.getMotionArgs(motionId);
assertEq(motionArgs.targets, targets);
assertEq(motionArgs.calldatas[0], calldatas[0]);
assertEq(motionArgs.signatures[0], signatures[0]);
assertEq(motionArgs.values[0], values[0]);

// cancel transfer motion
hoax(authorized);
transferfactory.cancelMotion(motionId);

// check lean track motion id was deleted
assertEq(leanTrack.motions(motionId).id, 0);
}

}
114 changes: 114 additions & 0 deletions foundry_test/BribesToSplitterMotionFactory.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// SPDX-License-Identifier: AGPL
pragma solidity ^0.8.16;

import "@openzeppelin/token/ERC20/ERC20.sol";
import {console} from "forge-std/console.sol";

import {ExtendedTest} from "./utils/ExtendedTest.sol";
import {VyperDeployer} from "../lib/utils/VyperDeployer.sol";
import {DualTimelock} from "./interfaces/DualTimelock.sol";
import {
LeanTrack,
Factory,
Motion
} from "./interfaces/LeanTrack.sol";
import {GovToken} from "./utils/GovToken.sol";
import {BribesToSplitterMotionFactory} from "../src/factories/examples/BribesToSplitterMotionFactory.sol";

import {MockLeanTrack, MotionArgs} from "./utils/MockLeanTrack.sol";

// these tests covers both the example BribesToSplitter and the BaseMotionFactory contracts
contract BribesToSplitterMotionFactoryTest is ExtendedTest {
address public immutable VOTER = 0xF147b8125d2ef93FB6965Db97D6746952a133934;
address public immutable SPLITTER = 0x527e80008D212E2891C737Ba8a2768a7337D7Fd2;
uint256 public constant DEFAULT_LIMIT = 1000;
VyperDeployer private vyperDeployer = new VyperDeployer();
DualTimelock private timelock;
ERC20 private token;
BribesToSplitterMotionFactory private factory;
MockLeanTrack private leanTrack;
GovToken private govToken;

uint256 public delay = 2 days;
uint256 public leanTrackDelay = 1 days;
uint256 public factoryMotionDuration = 1 days;

address public admin = address(1);
address public authorized = address(2);
address public objectoor = address(3);
address public mediumVoter = address(4);
address public whaleVoter1 = address(5);
address public whaleVoter2 = address(6);
address public knight = address(7);
address public smallVoter = address(8);
address public grantee = address(0xABCD);
address public executor = address(7);

event MotionCreated(
address[] targets,
uint256[] values,
string[] signatures,
bytes[] calldatas
);

function setUp() public {
// deploy token
govToken = new GovToken(18);
token = ERC20(govToken);

// deploy mock lean track
leanTrack = new MockLeanTrack();

factory = new BribesToSplitterMotionFactory(address(leanTrack), address(admin));

// set transfer limit
hoax(admin);
factory.setTransferLimit(address(token), DEFAULT_LIMIT);
// set authorized transfer motion creator
hoax(admin);
factory.setAuthorized(authorized, true);

// vm traces
vm.label(address(factory), "factory");
vm.label(address(leanTrack), "leanTrack");
vm.label(address(govToken), "govToken");
vm.label(address(token), "token");
}
function testSetup() public {
assertEq(address(factory.gov()), admin);
assertEq(address(factory.leanTrack()), address(leanTrack));

assertEq(factory.transferLimits(address(token)), DEFAULT_LIMIT);
assertEq(factory.authorized(authorized), true);
}

function testCreateTransferMotion() public {
address[] memory tokens = new address[](1);
tokens[0] = address(token);
uint256[] memory amounts = new uint256[](1);
amounts[0] = 100;

// create transfer motion
hoax(authorized);
uint256 motionId = factory.createBribesTransferMotion(tokens, amounts);

assertEq(motionId, 1);
address[] memory targets = new address[](1);
targets[0] = VOTER;
uint256[] memory values = new uint256[](1);
values[0] = 0;
string[] memory signatures = new string[](1);
bytes memory calldataForTransfer = abi.encodeWithSignature("transfer(address,uint256)", SPLITTER, 100);
bytes[] memory calldatas = new bytes[](1);
calldatas[0] = abi.encodeWithSignature("execute(address,uint256,bytes)", address(token), 0, calldataForTransfer);

// check lean track was called with expected params
MotionArgs memory motionArgs = leanTrack.getMotionArgs(motionId);
assertEq(motionArgs.id, motionId);
assertEq(motionArgs.targets, targets);
assertEq(motionArgs.calldatas[0], calldatas[0]);
assertEq(motionArgs.signatures[0], signatures[0]);
assertEq(motionArgs.values[0], values[0]);
}

}
Loading
Loading