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

[WIP]add pausable timelock #84

Open
wants to merge 1 commit into
base: main
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
448 changes: 448 additions & 0 deletions contracts/misc/PausableTimelockController.sol

Large diffs are not rendered by default.

40 changes: 40 additions & 0 deletions contracts/mock/MintableERC1155.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.8.4;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";

/**
* @title MintableERC1155
* @dev ERC1155 minting logic
*/
contract MintableERC1155 is Ownable, ERC1155 {
mapping(address => mapping(uint256 => uint256)) public mintCounts;

constructor() ERC1155("https://MintableERC1155/") {}

/**
* @dev Function to mint tokens
* @param id The id of tokens to mint.
* @return A boolean that indicates if the operation was successful.
*/
function mint(uint256 id, uint256 amount) public returns (bool) {
require(id > 0, "id is zero");
require(id <= 100, "exceed id limit");

mintCounts[_msgSender()][id] += amount;
require(mintCounts[_msgSender()][id] <= 10, "exceed mint limit");

_mint(_msgSender(), id, amount, new bytes(0));
return true;
}

function privateMint(uint256 id, uint256 amount) public onlyOwner returns (bool) {
_mint(_msgSender(), id, amount, new bytes(0));
return true;
}

function setURI(string memory uri_) public onlyOwner {
_setURI(uri_);
}
}
34 changes: 34 additions & 0 deletions contracts/mock/MockBlockContext.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.8.4;

/// @title MockBlockContext - helpers for block context
contract MockBlockContext {
// Helper functions
function getEthBalance(address addr) public view returns (uint256 balance) {
balance = addr.balance;
}

function getBlockHash(uint256 blockNumber) public view returns (bytes32 blockHash) {
blockHash = blockhash(blockNumber);
}

function getLastBlockHash() public view returns (bytes32 blockHash) {
blockHash = blockhash(block.number - 1);
}

function getCurrentBlockTimestamp() public view returns (uint256 timestamp) {
timestamp = block.timestamp;
}

function getCurrentBlockDifficulty() public view returns (uint256 difficulty) {
difficulty = block.difficulty;
}

function getCurrentBlockGasLimit() public view returns (uint256 gaslimit) {
gaslimit = block.gaslimit;
}

function getCurrentBlockCoinbase() public view returns (address coinbase) {
coinbase = block.coinbase;
}
}
65 changes: 65 additions & 0 deletions contracts/mock/MockTimelockTarget.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.8.4;

contract MockTimelockTarget {
string public sharedAnswer;
uint256 public sharedValue;

event MockFunctionCalled();
event MockFunctionCalledWithArgs(uint256 a, uint256 b);

uint256[] private _array;

function mockFunction() public payable returns (string memory) {
emit MockFunctionCalled();

return "0x1234";
}

function mockFunctionWithArgs(uint256 a, uint256 b) public payable returns (string memory) {
emit MockFunctionCalledWithArgs(a, b);

return "0x1234";
}

function mockFunctionNonPayable() public returns (string memory) {
emit MockFunctionCalled();

return "0x1234";
}

function mockStaticFunction() public pure returns (string memory) {
return "0x1234";
}

function mockFunctionRevertsNoReason() public payable {
revert();
}

function mockFunctionRevertsReason() public payable {
revert("CallReceiverMock: reverting");
}

function mockFunctionThrows() public payable {
assert(false);
}

function mockFunctionOutOfGas() public payable {
for (uint256 i = 0; ; ++i) {
_array.push(i);
}
}

function mockFunctionWritesStorage() public returns (string memory) {
sharedAnswer = "42";
return "0x1234";
}

function setValue(uint256 value) public {
sharedValue = value;
}

function getValue() public view returns (uint256) {
return sharedValue;
}
}
4 changes: 4 additions & 0 deletions deployments/deployed-contracts-goerli.json
Original file line number Diff line number Diff line change
Expand Up @@ -128,5 +128,9 @@
"MockLoanRepaidInterceptor": {
"address": "0xA3E10e91fEA0901C2d216B80a9E8C087aB85DECa",
"deployer": "0xafF5C36642385b6c7Aaf7585eC785aB2316b5db6"
},
"PausableTimelockController": {
"address": "0x61749537e6D4BC2c78acD4f0b538a3aE80e291DB",
"deployer": "0xafF5C36642385b6c7Aaf7585eC785aB2316b5db6"
}
}
1 change: 1 addition & 0 deletions helpers/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export const ONE_DAY = "86400";
export const ONE_HOUR = "3600";
export const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
export const ONE_ADDRESS = "0x0000000000000000000000000000000000000001";
export const ZERO_BYTES32 = "0x0000000000000000000000000000000000000000000000000000000000000000";
// ----------------
// PROTOCOL GLOBAL PARAMS
// ----------------
Expand Down
23 changes: 23 additions & 0 deletions helpers/contracts-deployments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import {
UiPoolDataProviderFactory,
BendCollectorFactory,
TimelockControllerFactory,
PausableTimelockControllerFactory,
WETH9,
WETH9Factory,
SupplyLogicFactory,
Expand Down Expand Up @@ -610,6 +611,28 @@ export const deployTimelockController = async (
verify
);

export const deployPausableTimelockController = async (
id: string,
minDelay: string,
proposers: string[],
executors: string[],
pausers: string[],
admin: string,
verify?: boolean
) =>
withSaveAndVerify(
await new PausableTimelockControllerFactory(await getDeploySigner()).deploy(
minDelay,
proposers,
executors,
pausers,
admin
),
id,
[minDelay, proposers, executors, pausers, admin],
verify
);

export const deployMockLoanRepaidInterceptor = async (addressesProvider: string, verify?: boolean) =>
withSaveAndVerify(
await new MockLoanRepaidInterceptorFactory(await getDeploySigner()).deploy(addressesProvider),
Expand Down
1 change: 1 addition & 0 deletions helpers/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ export enum eContractid {
TimelockControllerFast = "TimelockControllerFast",
TimelockControllerSlow = "TimelockControllerSlow",
MockLoanRepaidInterceptor = "MockLoanRepaidInterceptor",
PausableTimelockControllerTest = "PausableTimelockControllerTest",
}

export enum ProtocolLoanState {
Expand Down
36 changes: 36 additions & 0 deletions tasks/misc/timelock-task.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { task } from "hardhat/config";
import { deployPausableTimelockController } from "../../helpers/contracts-deployments";
import { eNetwork } from "../../helpers/types";

task("deploy:pausable-timelock", "Doing timelock admin task")
.addParam("id", "Contract ID")
.addParam("mindelay", "Minimum delay in seconds")
.addParam("proposers", "Addresses of proposers")
.addParam("executors", "Addresses of executors")
.addParam("pausers", "Addresses of pausers")
.addParam("admin", "Account of admin")
.setAction(async ({ id, mindelay, proposers, executors, pausers, admin }, DRE) => {
await DRE.run("set-DRE");

const network = DRE.network.name as eNetwork;

const proposerList = new String(proposers).split(",");
const executorList = new String(executors).split(",");
const pauserList = new String(pausers).split(",");

if (id == "" || id == undefined) {
id = "PausableTimelockController" + mindelay;
}

const timelock = await deployPausableTimelockController(
id,
mindelay,
proposerList,
executorList,
pauserList,
admin,
true
);

console.log("id:", id, timelock.address);
});
60 changes: 60 additions & 0 deletions test/helpers/expect-event.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import BigNumber from "bignumber.js";
import { BigNumber as BN } from "ethers";

import { expect } from "chai";

export function expectEvent(logs, eventName, eventArgs = {}) {
const events = logs.filter((e) => e.event === eventName);
expect(events.length > 0).to.equal(true, `No '${eventName}' events found`);

const exception: any[] = [];
const event = events.find(function (e) {
for (const [k, v] of Object.entries(eventArgs)) {
try {
contains(e.args, k, v);
} catch (error) {
exception.push(error);
return false;
}
}
return true;
});

if (event === undefined) {
throw exception[0];
}

return event;
}

export function notExpectEvent(logs, eventName) {
// eslint-disable-next-line no-unused-expressions
expect(
logs.find((e) => e.event === eventName),
`Event ${eventName} was found`
).to.be.undefined;
}

function isBN(object) {
return BN.isBigNumber(object) || object instanceof BN;
}

function contains(args, key, value) {
expect(key in args).to.equal(true, `Event argument '${key}' not found`);

if (value === null) {
expect(args[key]).to.equal(null, `expected event argument '${key}' to be null but got ${args[key]}`);
} else if (isBN(args[key]) || isBN(value)) {
const actual = isBN(args[key]) ? args[key].toString() : args[key];
const expected = isBN(value) ? value.toString() : value;
expect(args[key]).to.be.equal(
value,
`expected event argument '${key}' to have value ${expected} but got ${actual}`
);
} else {
expect(args[key]).to.be.deep.equal(
value,
`expected event argument '${key}' to have value ${value} but got ${args[key]}`
);
}
}
7 changes: 7 additions & 0 deletions test/helpers/make-suite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ import {
MockIncentivesController,
UiPoolDataProvider,
WalletBalanceProvider,
MockBlockContext,
MockBlockContextFactory,
} from "../../types";
import { MockChainlinkOracle } from "../../types/MockChainlinkOracle";
import { USD_ADDRESS } from "../../helpers/constants";
Expand Down Expand Up @@ -109,6 +111,8 @@ export interface TestEnv {

roundIdTracker: number;
nowTimeTracker: number;

mockBlockContext: MockBlockContext;
}

let buidlerevmSnapshotId: string = "0x1";
Expand Down Expand Up @@ -148,6 +152,7 @@ const testEnv: TestEnv = {
tokenIdTracker: {} as number,
roundIdTracker: {} as number,
nowTimeTracker: {} as number,
mockBlockContext: {} as MockBlockContext,
} as TestEnv;

export async function initializeMakeSuite() {
Expand Down Expand Up @@ -247,6 +252,8 @@ export async function initializeMakeSuite() {

testEnv.roundIdTracker = 1;
testEnv.nowTimeTracker = Number(await getNowTimeInSeconds());

testEnv.mockBlockContext = await new MockBlockContextFactory(testEnv.deployer.signer).deploy();
}

const setSnapshot = async () => {
Expand Down
Loading