-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
196 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity 0.8.17; | ||
|
||
import "../interfaces/IERC20.sol"; | ||
import "../interfaces/IBribe.sol"; | ||
|
||
contract BribeDistribution { | ||
|
||
string public constant VERSION = "1.0.0"; | ||
|
||
address public owner; | ||
address public pendingOwner; | ||
address public operator; | ||
|
||
IBribe public immutable bribe; | ||
address public immutable vault; | ||
address public immutable token; | ||
uint public round; | ||
|
||
constructor(address bribe_, address _vault, address _token) { | ||
bribe = IBribe(bribe_); | ||
vault = _vault; | ||
token = _token; | ||
owner = msg.sender; | ||
} | ||
|
||
modifier onlyOwner() { | ||
require(msg.sender == owner, "NOT_OWNER"); | ||
_; | ||
} | ||
|
||
modifier onlyOperator() { | ||
require(msg.sender == operator || msg.sender == owner, "NOT_OPERATOR"); | ||
_; | ||
} | ||
|
||
function offerOwnership(address newOwner) external onlyOwner { | ||
require(newOwner != address(0), "ZERO_ADDRESS"); | ||
pendingOwner = newOwner; | ||
} | ||
|
||
function acceptOwnership() external { | ||
require(msg.sender == pendingOwner, "NOT_OWNER"); | ||
owner = pendingOwner; | ||
} | ||
|
||
function setOperator(address operator_) external onlyOwner { | ||
operator = operator_; | ||
} | ||
|
||
////////////////// MAIN LOGIC ////////////////////// | ||
|
||
function autoNotify() external onlyOperator { | ||
_notify(IERC20(token).balanceOf(msg.sender), round % 2 == 0); | ||
round++; | ||
} | ||
|
||
function manualNotify(uint amount, bool fresh) external onlyOperator { | ||
_notify(amount, fresh); | ||
} | ||
|
||
function _notify(uint amount, bool fresh) internal { | ||
if (amount != 0) { | ||
IERC20(token).transferFrom(msg.sender, address(this), amount); | ||
} | ||
|
||
uint toBribes = IERC20(token).balanceOf(address(this)); | ||
require(toBribes != 0, "ZERO_BALANCE"); | ||
|
||
// assume we will have bribes once per 2 weeks. Need to use a half of the current balance in case of start of new 2 weeks epoch. | ||
if (fresh) { | ||
toBribes = toBribes / 2; | ||
} | ||
|
||
_approveIfNeed(token, address(bribe), toBribes); | ||
bribe.notifyForNextEpoch(vault, token, toBribes); | ||
} | ||
|
||
function _approveIfNeed(address _token, address dst, uint amount) internal { | ||
if (IERC20(_token).allowance(address(this), dst) < amount) { | ||
IERC20(_token).approve(dst, type(uint).max); | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import {ethers} from "hardhat"; | ||
import {DeployerUtils} from "../utils/DeployerUtils"; | ||
import {PolygonAddresses} from "../addresses/polygon"; | ||
import {Addresses} from "../addresses/addresses"; | ||
import {BribeDistribution} from "../../typechain"; | ||
|
||
async function main() { | ||
const signer = (await ethers.getSigners())[0]; | ||
const core = Addresses.getCore(); | ||
await DeployerUtils.deployContract(signer, "BribeDistribution", core.bribe, PolygonAddresses.tUSDC, core.tetu); | ||
} | ||
|
||
main() | ||
.then(() => process.exit(0)) | ||
.catch(error => { | ||
console.error(error); | ||
process.exit(1); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import chai from "chai"; | ||
import chaiAsPromised from "chai-as-promised"; | ||
import {SignerWithAddress} from "@nomiclabs/hardhat-ethers/signers"; | ||
import {BribeDistribution, MockBribe__factory, MockGauge__factory, MockToken,} from "../../typechain"; | ||
import {TimeUtils} from "../TimeUtils"; | ||
import {ethers} from "hardhat"; | ||
import {DeployerUtils} from "../../scripts/utils/DeployerUtils"; | ||
|
||
const {expect} = chai; | ||
chai.use(chaiAsPromised); | ||
|
||
describe("BribeDistributorTest", function () { | ||
let snapshotBefore: string; | ||
let snapshot: string; | ||
let signer: SignerWithAddress; | ||
let signer2: SignerWithAddress; | ||
|
||
let distr: BribeDistribution; | ||
let token: MockToken; | ||
|
||
|
||
before(async function () { | ||
this.timeout(1200000); | ||
snapshotBefore = await TimeUtils.snapshot(); | ||
[signer, signer2] = await ethers.getSigners(); | ||
|
||
const controller = await DeployerUtils.deployMockController(signer); | ||
const mockBribe = MockBribe__factory.connect(await DeployerUtils.deployProxy(signer, 'MockBribe'), signer); | ||
await mockBribe.init(controller.address); | ||
const mockGauge = MockGauge__factory.connect(await DeployerUtils.deployProxy(signer, 'MockGauge'), signer); | ||
await mockGauge.init(controller.address) | ||
|
||
token = await DeployerUtils.deployMockToken(signer); | ||
|
||
const vault = await DeployerUtils.deployTetuVaultV2( | ||
signer, | ||
controller.address, | ||
token.address, | ||
'TETU', | ||
'TETU', | ||
mockGauge.address, | ||
0 | ||
); | ||
|
||
distr = await DeployerUtils.deployContract(signer, "BribeDistribution", mockBribe.address, vault.address, token.address) as BribeDistribution; | ||
}) | ||
|
||
after(async function () { | ||
await TimeUtils.rollback(snapshotBefore); | ||
}); | ||
|
||
beforeEach(async function () { | ||
snapshot = await TimeUtils.snapshot(); | ||
}); | ||
|
||
afterEach(async function () { | ||
await TimeUtils.rollback(snapshot); | ||
}); | ||
|
||
it("set new owner test", async () => { | ||
await expect(distr.connect(signer2).offerOwnership(signer2.address)).revertedWith('NOT_OWNER'); | ||
await distr.offerOwnership(signer2.address) | ||
await expect(distr.acceptOwnership()).revertedWith('NOT_OWNER'); | ||
await distr.connect(signer2).acceptOwnership() | ||
expect(await distr.owner()).eq(signer2.address) | ||
await expect(distr.offerOwnership(signer2.address)).revertedWith('NOT_OWNER'); | ||
}) | ||
|
||
it("manualNotify test", async () => { | ||
await token.approve(distr.address, 1000) | ||
const bribe = await distr.bribe(); | ||
|
||
await distr.manualNotify(1000, true) | ||
expect(await token.balanceOf(bribe)).eq(500); | ||
|
||
await distr.manualNotify(0, false) | ||
expect(await token.balanceOf(bribe)).eq(1000); | ||
}) | ||
|
||
it("autoNotify test", async () => { | ||
const bal = await token.balanceOf(signer.address); | ||
await token.approve(distr.address, bal) | ||
const bribe = await distr.bribe(); | ||
|
||
await distr.autoNotify() | ||
expect(await token.balanceOf(bribe)).eq(bal.div(2)); | ||
|
||
await distr.autoNotify() | ||
expect(await token.balanceOf(bribe)).eq(bal); | ||
}) | ||
}) |