Skip to content
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
56 changes: 52 additions & 4 deletions contracts/general/Ownable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,19 @@ pragma solidity ^0.6.6;
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*
* @dev Completely default OpenZeppelin.
*
* @dev We've added a second owner to share control of the timelocked owner contract.
*/
contract Ownable {
address private _owner;
address private _pendingOwner;

// Second allows a DAO to share control.
address private _secondOwner;
address private _pendingSecond;

event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
event SecondOwnershipTransferred(address indexed previousOwner, address indexed newOwner);

/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
Expand All @@ -22,7 +27,9 @@ contract Ownable {
function initializeOwnable() internal {
require(_owner == address(0), "already initialized");
_owner = msg.sender;
_secondOwner = msg.sender;
emit OwnershipTransferred(address(0), msg.sender);
emit SecondOwnershipTransferred(address(0), msg.sender);
}


Expand All @@ -33,27 +40,44 @@ contract Ownable {
return _owner;
}

/**
* @return the address of the owner.
*/
function secondOwner() public view returns (address) {
return _secondOwner;
}

/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(isOwner(), "msg.sender is not owner");
_;
}

modifier onlyFirstOwner() {
require(msg.sender == _owner, "msg.sender is not owner");
_;
}

modifier onlySecondOwner() {
require(msg.sender == _secondOwner, "msg.sender is not owner");
_;
}

/**
* @return true if `msg.sender` is the owner of the contract.
*/
function isOwner() public view returns (bool) {
return msg.sender == _owner;
return msg.sender == _owner || msg.sender == _secondOwner;

}

/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
function transferOwnership(address newOwner) public onlyFirstOwner {
_pendingOwner = newOwner;
}

Expand All @@ -73,6 +97,30 @@ contract Ownable {
_owner = newOwner;
}

/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function transferSecondOwnership(address newOwner) public onlySecondOwner {
_pendingSecond = newOwner;
}

function receiveSecondOwnership() public {
require(msg.sender == _pendingSecond, "only pending owner can call this function");
_transferSecondOwnership(_pendingSecond);
_pendingSecond = address(0);
}

/**
* @dev Transfers control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function _transferSecondOwnership(address newOwner) internal {
require(newOwner != address(0));
emit SecondOwnershipTransferred(_secondOwner, newOwner);
_secondOwner = newOwner;
}

uint256[50] private __gap;
}

31 changes: 20 additions & 11 deletions contracts/staking/FarmController.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ contract FarmController is Ownable {

address public constant borrowerReward = 0x1337DEF172152f2fF82d9545Fd6f79fE38dF15ce;

uint256 public constant INITIAL_DISTRIBUTED = 1638082800;
uint256 public constant INITIAL_DISTRIBUTED = 1638687600;

IRewardDistributionRecipientTokenOnly[] public farms;
mapping(address => address) public lpFarm;
Expand Down Expand Up @@ -84,7 +84,7 @@ contract FarmController is Ownable {
}

function initializeRewardDistribution() external onlyOwner {
require(lastRewardDistributed > 0, "initialized");
require(lastRewardDistributed == 0, "initialized");
lastRewardDistributed = INITIAL_DISTRIBUTED;
}

Expand All @@ -96,21 +96,30 @@ contract FarmController is Ownable {
for(uint256 i = 0; i<lpFarms.length; i++){
IRewardDistributionRecipientTokenOnly farm = lpFarms[i];
uint256 amount = cacheLpReward.mul(rate[address(farm)]).div(cacheSum);
rewardToken.approve(address(farm), amount);
farm.notifyRewardAmount(amount);
if(amount > 0) {
rewardToken.approve(address(farm), amount);
farm.notifyRewardAmount(amount);
}
}

uint256 cacheStakerReward = stakerRewards;
IRewardDistributionRecipientTokenOnly stakerFarm = IRewardDistributionRecipientTokenOnly(stakerReward);
rewardToken.approve(address(stakerFarm), cacheStakerReward);
stakerFarm.notifyRewardAmount(cacheStakerReward);
if(cacheStakerReward > 0) {
IRewardDistributionRecipientTokenOnly stakerFarm = IRewardDistributionRecipientTokenOnly(stakerReward);
rewardToken.approve(address(stakerFarm), cacheStakerReward);
stakerFarm.notifyRewardAmount(cacheStakerReward);
}

uint256 cacheBorrowerReward = borrowerRewards;
IRewardDistributionRecipientTokenOnly borrowerFarm = IRewardDistributionRecipientTokenOnly(borrowerReward);
rewardToken.approve(address(borrowerFarm), cacheBorrowerReward);
stakerFarm.notifyRewardAmount(cacheBorrowerReward);
if(cacheBorrowerReward > 0) {
IRewardDistributionRecipientTokenOnly borrowerFarm = IRewardDistributionRecipientTokenOnly(borrowerReward);
rewardToken.approve(address(borrowerFarm), cacheBorrowerReward);
borrowerFarm.notifyRewardAmount(cacheBorrowerReward);
}

lastRewardDistributed += 7 days;
// this will make sure lastRewardDistributed to set in order
while(block.timestamp - lastRewardDistributed > 7 days) {
lastRewardDistributed += 7 days;
}
}

// should transfer rewardToken prior to calling this contract
Expand Down
2 changes: 1 addition & 1 deletion test/core/RewardManagerV2_mainnet.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ describe("RewardManagerV2 Mainnet Fork", function () {
expect(userInfo.amount).to.equal(coverPrice);
});

it.only("should withdraw from rewardV2 when withdrawNft", async function () {
it("should withdraw from rewardV2 when withdrawNft", async function () {
// Allow user
const userAddress = "0x09fa38eba245bb68354b8950fa2fe71f02863393";
const protocol = "0x0000000000000000000000000000000000000001";
Expand Down
59 changes: 59 additions & 0 deletions test/fork.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { network, ethers } from "hardhat";
import { providers, Contract, Signer, BigNumber, utils } from "ethers";
import { expect } from "chai";
import { mine, increase, getTimestamp } from './utils';

const multisig_mainnet = "0x1f28ed9d4792a567dad779235c2b766ab84d8e33";
const controller_mainnet = "0x1337DEF159da6F97dB7c4D0E257dc689837b9E70";
const staker_mainnet = "0x1337DEF1B1Ae35314b40e5A4b70e216A499b0E37";
const borrower_mainnet = "0x1337DEF172152f2fF82d9545Fd6f79fE38dF15ce";
const token_mainnet = "0x1337def16f9b486faed0293eb623dc8395dfe46a";
let multisig : Signer;

let farmController : Contract;
let stakerFarm : Contract;
let borrowerFarm : Contract;
let token: Contract;

describe.only('fork', function(){
beforeEach(async function(){
await network.provider.request({
method: "hardhat_impersonateAccount",
params: [multisig_mainnet]
});
multisig = await ethers.provider.getSigner(multisig_mainnet);
const proxy = await ethers.getContractAt("OwnedUpgradeabilityProxy", controller_mainnet);
const NewTemplate = await ethers.getContractFactory("FarmController");
const template = await NewTemplate.deploy();
await proxy.connect(multisig).upgradeTo(template.address);
farmController = await ethers.getContractAt("FarmController", proxy.address);
await farmController.connect(multisig).initializeRewardDistribution();
stakerFarm = await ethers.getContractAt("UtilizationFarm", staker_mainnet);
borrowerFarm = await ethers.getContractAt("UtilizationFarm", borrower_mainnet);
token = await ethers.getContractAt("contracts/interfaces/IERC20.sol:IERC20", token_mainnet);
await stakerFarm.connect(multisig).setRewardDistribution(proxy.address);
await borrowerFarm.connect(multisig).setRewardDistribution(proxy.address);
});

it.only('changeProtocol - stakeManual', async function() {
console.log((await getTimestamp()).toString());
await increase( 3* 86400 + 3 * 60 * 60);
await mine();
console.log((await getTimestamp()).toString());
await farmController.connect(multisig).setRewards("10000", "100", "1");
await token.connect(multisig).transfer(farmController.address, "10101");
const beforeBalance = {
staker: await token.balanceOf(staker_mainnet),
borrower : await token.balanceOf(borrower_mainnet)
}
await farmController.flushRewards();
const afterBalance = {
staker: await token.balanceOf(staker_mainnet),
borrower : await token.balanceOf(borrower_mainnet)
}

expect(afterBalance.staker).to.equal(beforeBalance.staker.add(100));
expect(afterBalance.borrower).to.equal(beforeBalance.borrower.add(1));
console.log((await farmController.lastRewardDistributed()).toString());
});
});