Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
18 changes: 18 additions & 0 deletions deploy/FlapperDeploy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
pragma solidity ^0.8.21;

import "dss-interfaces/Interfaces.sol";
import { MCD, DssInstance } from "dss-test/MCD.sol";
import { ScriptTools } from "dss-test/ScriptTools.sol";

import { SplitterInstance } from "./SplitterInstance.sol";
Expand All @@ -25,9 +26,12 @@ import { FlapperUniV2SwapOnly } from "src/FlapperUniV2SwapOnly.sol";
import { SplitterMom } from "src/SplitterMom.sol";
import { OracleWrapper } from "src/OracleWrapper.sol";
import { Splitter } from "src/Splitter.sol";
import { Kicker } from "src/Kicker.sol";

library FlapperDeploy {

address constant LOG = 0xdA0Ab1e0017DEbCd72Be8599041a2aa3bA7e740F;

function deployFlapperUniV2(
address deployer,
address owner,
Expand Down Expand Up @@ -68,4 +72,18 @@ library FlapperDeploy {
splitterInstance.splitter = splitter;
splitterInstance.mom = mom;
}

function deployKicker(
address deployer,
address owner
) internal returns (address kicker) {
DssInstance memory dss = MCD.loadFromChainlog(LOG);

kicker = address(new Kicker(
dss.chainlog.getAddress("MCD_VAT"),
dss.chainlog.getAddress("MCD_VOW"),
dss.chainlog.getAddress("MCD_SPLIT")));

ScriptTools.switchOwner(kicker, deployer, owner);
}
}
39 changes: 39 additions & 0 deletions deploy/FlapperInit.sol
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ interface SplitterLike {
function usdsJoin() external view returns (address);
function hop() external view returns (uint256);
function rely(address) external;
function deny(address) external;
function file(bytes32, uint256) external;
function file(bytes32, address) external;
}
Expand All @@ -70,6 +71,16 @@ interface FarmLike {
function setRewardsDuration(uint256) external;
}

interface KickerLike {
function file(bytes32, uint256) external;
function file(bytes32, int256) external;
}

interface VowLike {
function sump() external view returns (uint256);
function file(bytes32, uint256) external;
}

struct FlapperUniV2Config {
uint256 want;
address pip;
Expand Down Expand Up @@ -99,6 +110,12 @@ struct SplitterConfig {
bytes32 momChainlogKey;
}

struct KickerConfig {
int256 khump;
uint256 kbump;
bytes32 chainlogKey;
}

library FlapperInit {
uint256 constant WAD = 10 ** 18;
uint256 constant RAY = 10 ** 27;
Expand Down Expand Up @@ -208,4 +225,26 @@ library FlapperInit {
if (cfg.prevMomChainlogKey != bytes32(0)) dss.chainlog.removeAddress(cfg.prevMomChainlogKey);
dss.chainlog.setAddress(cfg.momChainlogKey, address(mom));
}

function initKicker(
DssInstance memory dss,
address kicker,
KickerConfig memory cfg
) internal {
require(cfg.kbump % RAY == 0, "kbump not multiple of RAY");

VowLike vow = VowLike(dss.chainlog.getAddress("MCD_VOW"));
require(vow.sump() == type(uint256).max, "flop is not inactive");

vow.file("bump", 0);
vow.file("hump", type(uint256).max);

KickerLike(kicker).file("khump", cfg.khump);
KickerLike(kicker).file("kbump", cfg.kbump);

dss.vat.rely(kicker);
SplitterLike(dss.chainlog.getAddress("MCD_SPLIT")).rely(kicker);

dss.chainlog.setAddress(cfg.chainlogKey, kicker);
}
}
109 changes: 109 additions & 0 deletions src/Kicker.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// SPDX-FileCopyrightText: © 2025 Dai Foundation <www.daifoundation.org>
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

pragma solidity ^0.8.21;

interface VatLike {
function dai(address) external view returns (uint256);
function sin(address) external view returns (uint256);
function hope(address) external;
function suck(address, address, uint256) external;
}

interface SplitterLike {
function kick(uint256, uint256) external returns (uint256);
}

contract Kicker {
// --- storage variables ---

mapping(address usr => uint256 allowed) public wards;
uint256 public kbump; // Fixed lot size [rad]
int256 public khump; // Allowance limit [rad]

// --- immutables ---

VatLike public immutable vat;
address public immutable vow;
SplitterLike public immutable splitter;

// --- constructor ---

constructor(address vat_, address vow_, address splitter_) {
vat = VatLike(vat_);
vow = vow_;
splitter = SplitterLike(splitter_);
vat.hope(splitter_);

wards[msg.sender] = 1;
emit Rely(msg.sender);
}

// --- events ---

event Rely(address indexed usr);
event Deny(address indexed usr);
event File(bytes32 indexed what, uint256 data);
event File(bytes32 indexed what, int256 data);

// --- modifiers ---

modifier auth {
require(wards[msg.sender] == 1, "Kicker/not-authorized");
_;
}

// --- internals ---

function _toInt256(uint256 x) internal pure returns (int256 y) {
require(x <= uint256(type(int256).max), "Kicker/overflow");
y = int256(x);
}

// --- administration ---

function rely(address usr) external auth {
wards[usr] = 1;
emit Rely(usr);
}

function deny(address usr) external auth {
wards[usr] = 0;
emit Deny(usr);
}

function file(bytes32 what, uint256 data) external auth {
if (what == "kbump") {
kbump = data;
} else revert("Kicker/file-unrecognized-param");
emit File(what, data);
}

function file(bytes32 what, int256 data) external auth {
if (what == "khump") {
khump = data;
} else revert("Kicker/file-unrecognized-param");
emit File(what, data);
}

// --- execution ---

function flap() external returns (uint256 id) {
require(_toInt256(vat.dai(address(vow))) >= _toInt256(vat.sin(address(vow))) + _toInt256(kbump) + khump, "Kicker/insufficient-allowance");
vat.suck(address(vow), address(this), kbump);
id = splitter.kick(kbump, 0);
}
}
Loading