-
Notifications
You must be signed in to change notification settings - Fork 99
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
skimmer #457
Merged
Merged
skimmer #457
Changes from 2 commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,62 @@ | ||
// SPDX-License-Identifier: GPL-3.0-or-later | ||
pragma solidity ^0.8.4; | ||
|
||
import "../IPCVDeposit.sol"; | ||
import "../../refs/CoreRef.sol"; | ||
|
||
/// @title a contract to skim excess FEI from addresses | ||
/// @author Fei Protocol | ||
contract FeiSkimmer is CoreRef { | ||
|
||
event ThresholdUpdate(uint256 newThreshold); | ||
|
||
/// @notice source PCV deposit to skim excess FEI from | ||
IPCVDeposit public immutable source; | ||
|
||
/// @notice the threshold of FEI above which to skim | ||
uint256 public threshold; | ||
|
||
/// @notice FEI Skimmer | ||
/// @param _core Fei Core for reference | ||
/// @param _source the target to skim from | ||
/// @param _threshold the threshold of FEI to be maintained by source | ||
constructor( | ||
address _core, | ||
IPCVDeposit _source, | ||
uint256 _threshold | ||
) | ||
CoreRef(_core) | ||
{ | ||
source = _source; | ||
threshold = _threshold; | ||
emit ThresholdUpdate(threshold); | ||
} | ||
|
||
/// @return true if FEI balance of source exceeds threshold | ||
function skimEligible() external view returns (bool) { | ||
return fei().balanceOf(address(source)) > threshold; | ||
} | ||
|
||
/// @notice skim FEI above the threshold from the source. Pausable. Requires skimEligible() | ||
function skim() | ||
external | ||
whenNotPaused | ||
{ | ||
IFei _fei = fei(); | ||
uint256 feiTotal = _fei.balanceOf(address(source)); | ||
|
||
require(feiTotal > threshold, "under threshold"); | ||
|
||
uint256 burnAmount = feiTotal - threshold; | ||
source.withdrawERC20(address(_fei), address(this), burnAmount); | ||
|
||
_fei.burn(burnAmount); | ||
} | ||
|
||
/// @notice set the threshold for FEI skims. Only Governor or Admin | ||
/// @param newThreshold the new value above which FEI is skimmed. | ||
function setThreshold(uint256 newThreshold) external onlyGovernorOrAdmin { | ||
threshold = newThreshold; | ||
emit ThresholdUpdate(newThreshold); | ||
} | ||
} |
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,83 @@ | ||
import { Core, FeiSkimmer, MockPCVDepositV2 } from '@custom-types/contracts'; | ||
import { expectRevert, getAddresses, getCore, getImpersonatedSigner } from '@test/helpers'; | ||
import { expect } from 'chai'; | ||
import { Signer } from 'ethers'; | ||
import { ethers } from 'hardhat'; | ||
|
||
describe.only('FeiSkimmer', function () { | ||
let minterAddress: string; | ||
let userAddress: string; | ||
let governorAddress: string; | ||
let core: Core; | ||
let skimmer: FeiSkimmer; | ||
let source: MockPCVDepositV2; | ||
|
||
const threshold = ethers.constants.WeiPerEther; | ||
|
||
const impersonatedSigners: { [key: string]: Signer } = {}; | ||
|
||
before(async () => { | ||
const addresses = await getAddresses(); | ||
|
||
// add any addresses you want to impersonate here | ||
const impersonatedAddresses = [addresses.userAddress, addresses.governorAddress, addresses.minterAddress]; | ||
|
||
for (const address of impersonatedAddresses) { | ||
impersonatedSigners[address] = await getImpersonatedSigner(address); | ||
} | ||
}); | ||
|
||
beforeEach(async function () { | ||
({ userAddress, governorAddress, minterAddress } = await getAddresses()); | ||
core = await getCore(); | ||
|
||
source = await (await ethers.getContractFactory('MockPCVDepositV2')).deploy(core.address, await core.fei(), 0, 0); | ||
|
||
skimmer = await (await ethers.getContractFactory('FeiSkimmer')).deploy(core.address, source.address, threshold); | ||
}); | ||
|
||
describe('Initial configuration', function () { | ||
it('has source set', async function () { | ||
expect(await skimmer.source()).to.be.equal(source.address); | ||
}); | ||
|
||
it('has threshold set', async function () { | ||
expect(await skimmer.threshold()).to.be.equal(threshold); | ||
}); | ||
|
||
it('is not skim eligible', async function () { | ||
expect(await skimmer.skimEligible()).to.be.false; | ||
}); | ||
}); | ||
|
||
describe('Skim', function () { | ||
Joeysantoro marked this conversation as resolved.
Show resolved
Hide resolved
|
||
it('is eligible and functional over threshold', async function () { | ||
const fei = await ethers.getContractAt('IFei', await core.fei()); | ||
|
||
await fei.connect(impersonatedSigners[minterAddress]).mint(source.address, ethers.constants.WeiPerEther.mul(2)); | ||
|
||
expect(await skimmer.skimEligible()).to.be.true; | ||
|
||
await skimmer.skim(); | ||
|
||
expect(await fei.balanceOf(source.address)).to.be.equal(threshold); | ||
}); | ||
}); | ||
|
||
describe('Set Threshold', function () { | ||
it('from governor succeeds', async function () { | ||
expect(await skimmer.threshold()).to.be.equal(threshold); | ||
|
||
await skimmer.connect(impersonatedSigners[governorAddress]).setThreshold(0); | ||
|
||
expect(await skimmer.threshold()).to.be.equal(0); | ||
}); | ||
|
||
it('not from governor succeeds', async function () { | ||
await expectRevert( | ||
skimmer.connect(impersonatedSigners[userAddress]).setThreshold(0), | ||
'CoreRef: Caller is not a governor or contract admin' | ||
); | ||
}); | ||
}); | ||
}); |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove .only