Skip to content

Commit

Permalink
Configurable exit fee
Browse files Browse the repository at this point in the history
  • Loading branch information
oldchili committed Jul 14, 2024
1 parent 39dbea9 commit e4bdfee
Show file tree
Hide file tree
Showing 6 changed files with 24 additions and 15 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ The following functions are called from the LockstakeClipper (see below) through

* `farms` - Whitelisted set of farms to choose from.
* `jug` - The Dai lending rate calculation module.
* `fee` - Exit fee.


## 2. LockstakeClipper
Expand Down
3 changes: 1 addition & 2 deletions deploy/LockstakeDeploy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,13 @@ library LockstakeDeploy {
address voteDelegateFactory,
address nstJoin,
bytes32 ilk,
uint256 fee,
address mkrNgt,
bytes4 calcSig
) internal returns (LockstakeInstance memory lockstakeInstance) {
DssInstance memory dss = MCD.loadFromChainlog(0xdA0Ab1e0017DEbCd72Be8599041a2aa3bA7e740F);

lockstakeInstance.lsmkr = address(new LockstakeMkr());
lockstakeInstance.engine = address(new LockstakeEngine(voteDelegateFactory, nstJoin, ilk, mkrNgt, lockstakeInstance.lsmkr, fee));
lockstakeInstance.engine = address(new LockstakeEngine(voteDelegateFactory, nstJoin, ilk, mkrNgt, lockstakeInstance.lsmkr));
lockstakeInstance.clipper = address(new LockstakeClipper(address(dss.vat), address(dss.spotter), address(dss.dog), lockstakeInstance.engine));
(bool ok, bytes memory returnV) = dss.chainlog.getAddress("CALC_FAB").call(abi.encodeWithSelector(calcSig, owner));
require(ok);
Expand Down
3 changes: 2 additions & 1 deletion deploy/LockstakeInit.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ interface LockstakeEngineLike {
function ngt() external view returns (address);
function rely(address) external;
function file(bytes32, address) external;
function file(bytes32, uint256) external;
function addFarm(address) external;
}

Expand Down Expand Up @@ -150,7 +151,6 @@ library LockstakeInit {
require(engine.ilk() == cfg.ilk, "Engine ilk mismatch");
require(engine.mkr() == cfg.mkr, "Engine mkr mismatch");
require(engine.lsmkr() == lockstakeInstance.lsmkr, "Engine lsmkr mismatch");
require(engine.fee() == cfg.fee, "Engine fee mismatch");
require(engine.mkrNgt() == cfg.mkrNgt, "Engine mkrNgt mismatch");
require(engine.ngt() == cfg.ngt, "Engine ngt mismatch");
require(clipper.ilk() == cfg.ilk, "Clipper ilk mismatch");
Expand Down Expand Up @@ -207,6 +207,7 @@ library LockstakeInit {
LockstakeMkrLike(lockstakeInstance.lsmkr).rely(address(engine));

engine.file("jug", address(dss.jug));
engine.file("fee", cfg.fee);
for (uint256 i = 0; i < cfg.farms.length; i++) {
require(StakingRewardsLike(cfg.farms[i]).stakingToken() == lockstakeInstance.lsmkr, "Farm staking token mismatch");
engine.addFarm(cfg.farms[i]);
Expand Down
18 changes: 13 additions & 5 deletions src/LockstakeEngine.sol
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ contract LockstakeEngine is Multicall {
mapping(address urn => address farm) public urnFarms;
mapping(address urn => uint256 auctionsCount) public urnAuctions;
JugLike public jug;
uint256 public fee;

// --- constants and enums ---

Expand All @@ -92,7 +93,6 @@ contract LockstakeEngine is Multicall {
bytes32 immutable public ilk;
GemLike immutable public mkr;
GemLike immutable public lsmkr;
uint256 immutable public fee;
MkrNgtLike immutable public mkrNgt;
GemLike immutable public ngt;
uint256 immutable public mkrNgtRate;
Expand All @@ -103,6 +103,7 @@ contract LockstakeEngine is Multicall {
event Rely(address indexed usr);
event Deny(address indexed usr);
event File(bytes32 indexed what, address data);
event File(bytes32 indexed what, uint256 data);
event AddFarm(address farm);
event DelFarm(address farm);
event Open(address indexed owner, uint256 indexed index, address urn);
Expand Down Expand Up @@ -136,8 +137,7 @@ contract LockstakeEngine is Multicall {

// --- constructor ---

constructor(address voteDelegateFactory_, address nstJoin_, bytes32 ilk_, address mkrNgt_, address lsmkr_, uint256 fee_) {
require(fee_ < WAD, "LockstakeEngine/fee-equal-or-greater-wad");
constructor(address voteDelegateFactory_, address nstJoin_, bytes32 ilk_, address mkrNgt_, address lsmkr_) {
voteDelegateFactory = VoteDelegateFactoryLike(voteDelegateFactory_);
nstJoin = NstJoinLike(nstJoin_);
vat = nstJoin.vat();
Expand All @@ -148,7 +148,6 @@ contract LockstakeEngine is Multicall {
ngt = mkrNgt.ngt();
mkrNgtRate = mkrNgt.rate();
lsmkr = GemLike(lsmkr_);
fee = fee_;
urnImplementation = address(new LockstakeUrn(address(vat), lsmkr_));
vat.hope(nstJoin_);
nst.approve(nstJoin_, type(uint256).max);
Expand Down Expand Up @@ -206,6 +205,14 @@ contract LockstakeEngine is Multicall {
emit File(what, data);
}

function file(bytes32 what, uint256 data) external auth {
if (what == "fee") {
require(data < WAD, "LockstakeEngine/fee-equal-or-greater-wad");
fee = data;
} else revert("LockstakeEngine/file-unrecognized-param");
emit File(what, data);
}

function addFarm(address farm) external auth {
farms[farm] = FarmStatus.ACTIVE;
emit AddFarm(farm);
Expand Down Expand Up @@ -439,7 +446,8 @@ contract LockstakeEngine is Multicall {
uint256 burn;
uint256 refund;
if (left > 0) {
burn = _min(sold * fee / (WAD - fee), left);
uint256 fee_ = fee;
burn = _min(sold * fee_ / (WAD - fee_), left);
mkr.burn(address(this), burn);
unchecked { refund = left - burn; }
if (refund > 0) {
Expand Down
3 changes: 2 additions & 1 deletion test/LockstakeEngine-invariants.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,9 @@ contract LockstakeEngineIntegrationTest is DssTest {
vm.prank(voter1); voterDelegate1 = delFactory.create();

vm.startPrank(pauseProxy);
engine = new LockstakeEngine(address(delFactory), address(nstJoin), ilk, address(mkrNgt), address(lsmkr), 15 * WAD / 100);
engine = new LockstakeEngine(address(delFactory), address(nstJoin), ilk, address(mkrNgt), address(lsmkr));
engine.file("jug", jug);
engine.file("fee", 15 * WAD / 100);
vat.rely(address(engine));
vat.init(ilk);
JugLike(jug).init(ilk);
Expand Down
11 changes: 5 additions & 6 deletions test/LockstakeEngine.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,6 @@ contract LockstakeEngineTest is DssTest {
address(voteDelegateFactory),
address(nstJoin),
ilk,
15 * WAD / 100,
address(mkrNgt),
bytes4(abi.encodeWithSignature("newLinearDecrease(address)"))
);
Expand Down Expand Up @@ -324,7 +323,6 @@ contract LockstakeEngineTest is DssTest {
address(voteDelegateFactory),
address(nstJoin),
"eee",
15 * WAD / 100,
address(mkrNgt),
bytes4(abi.encodeWithSignature("newStairstepExponentialDecrease(address)"))
);
Expand All @@ -343,19 +341,16 @@ contract LockstakeEngineTest is DssTest {

function testConstructor() public {
address lsmkr2 = address(new GemMock(0));
vm.expectRevert("LockstakeEngine/fee-equal-or-greater-wad");
new LockstakeEngine(address(voteDelegateFactory), address(nstJoin), "aaa", address(mkrNgt), lsmkr2, WAD);
vm.expectEmit(true, true, true, true);
emit Rely(address(this));
LockstakeEngine e = new LockstakeEngine(address(voteDelegateFactory), address(nstJoin), "aaa", address(mkrNgt), lsmkr2, 100);
LockstakeEngine e = new LockstakeEngine(address(voteDelegateFactory), address(nstJoin), "aaa", address(mkrNgt), lsmkr2);
assertEq(address(e.voteDelegateFactory()), address(voteDelegateFactory));
assertEq(address(e.nstJoin()), address(nstJoin));
assertEq(address(e.vat()), address(dss.vat));
assertEq(address(e.nst()), address(nst));
assertEq(e.ilk(), "aaa");
assertEq(address(e.mkr()), address(mkr));
assertEq(address(e.lsmkr()), lsmkr2);
assertEq(e.fee(), 100);
assertEq(address(e.mkrNgt()), address(mkrNgt));
assertEq(address(e.ngt()), address(ngt));
assertEq(e.mkrNgtRate(), 24_000);
Expand All @@ -375,6 +370,10 @@ contract LockstakeEngineTest is DssTest {

function testFile() public {
checkFileAddress(address(engine), "LockstakeEngine", ["jug"]);
checkFileUint(address(engine), "LockstakeEngine", ["fee"]);

vm.expectRevert("LockstakeEngine/fee-equal-or-greater-wad");
vm.prank(pauseProxy); engine.file("fee", WAD);
}

function testModifiers() public {
Expand Down

0 comments on commit e4bdfee

Please sign in to comment.