From 66a02d3f5291eeb11d11302400b1862d5a31e80e Mon Sep 17 00:00:00 2001 From: yaron velner Date: Mon, 29 Nov 2021 23:01:57 +0200 Subject: [PATCH 1/5] bug fix when barking with low ink --- src/blip.sol | 2 +- src/blip.t.sol | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/blip.sol b/src/blip.sol index cbc8243..25b6952 100644 --- a/src/blip.sol +++ b/src/blip.sol @@ -72,7 +72,7 @@ contract Blipper is Clipper { uint256 ask = rmul(tab, WAD) / rdiv(mid, bee); // how much dai to get for the entire collateral - uint256 bid = mul(wmul(lot, rmul(mid, bee)), RAY); + uint256 bid = rdiv(mul(wmul(lot, mid), RAY), bee); if(ask <= lot) { amt = ask; diff --git a/src/blip.t.sol b/src/blip.t.sol index 7004095..7aea153 100644 --- a/src/blip.t.sol +++ b/src/blip.t.sol @@ -90,8 +90,9 @@ contract BammJoinTest is DssDeployTestBase { assertEq(vat.gem("ETH", address(bamm)), expectedEth, "gem balance low ink"); assertEq(vat.dai(address(0x123)), 1e27); - uint daiDebt = uint(100 ether * 110 * 105) / 100; - assertEq(vat.dai(address(vow)), daiDebt * 1e27); + // 100 ether to dai with 5% premium + uint daiDebt = 1e27 * uint(100 ether * 110 * 100) / 105; + assertEq(vat.dai(address(vow)), daiDebt); assertEq(dog.Dirt(), 0); (,,,uint dirt) = dog.ilks("ETH"); assertEq(dirt, 0); From 59d665ef24d99a4813ea1f16f570386d79dc8663 Mon Sep 17 00:00:00 2001 From: yaron velner Date: Tue, 30 Nov 2021 11:42:51 +0200 Subject: [PATCH 2/5] remove clipper --- src/blip.sol | 155 +++++++++++++++++++++++++++++++++++++++---------- src/blip.t.sol | 25 +++++--- 2 files changed, 141 insertions(+), 39 deletions(-) diff --git a/src/blip.sol b/src/blip.sol index 25b6952..2605a2e 100644 --- a/src/blip.sol +++ b/src/blip.sol @@ -2,7 +2,17 @@ pragma solidity >=0.6.12; -import "./org/clip.sol"; +interface VatLike { + function move(address,address,uint256) external; + function flux(bytes32,address,address,uint256) external; + function ilks(bytes32) external returns (uint256, uint256, uint256, uint256, uint256); + function suck(address,address,uint256) external; +} + +interface DogLike { + function chop(bytes32) external returns (uint256); + function digs(bytes32, uint256) external; +} interface OracleLike { function read() external returns (bytes32); @@ -12,46 +22,117 @@ interface BProtocolLike { function prep(bytes32 ilk, uint256 amt, uint256 owe, uint256 mid) external; } +interface ClipperLike { + function dog() external view returns(DogLike); + function vow() external view returns(address); + function chip() external view returns(uint64); + function tip() external view returns(uint192); + function stopped() external view returns(uint256); + + function kick( + uint256 tab, + uint256 lot, + address usr, + address kpr + ) external returns (uint256 id); +} + + +contract Blipper { + // --- Auth --- + mapping (address => uint256) public wards; + function rely(address usr) external auth { wards[usr] = 1; emit Rely(usr); } + function deny(address usr) external auth { wards[usr] = 0; emit Deny(usr); } + modifier auth { + require(wards[msg.sender] == 1, "Blipper/not-authorized"); + _; + } + + // --- Clipper Data --- + bytes32 immutable public ilk; // Collateral type of this Clipper + VatLike immutable public vat; // Core CDP Engine + DogLike public dog; // Liquidation module + address public vow; // Recipient of dai raised in auctions + uint64 public chip; // Percentage of tab to suck from vow to incentivize keepers [wad] + uint192 public tip; // Flat fee to suck from vow to incentivize keepers [rad] -contract Blipper is Clipper { + // --- B.Protocol Data --- + address public clipper; address public bprotocol; uint256 public bee; // b.protocol discount address public oracle; + // --- Events --- + event Rely(address indexed usr); + event Deny(address indexed usr); + + event File(bytes32 indexed what, uint256 data); + event File(bytes32 indexed what, address data); + + event Blip( + uint256 tab, + uint256 lot, + address usr, + address kpr, + uint256 amt, + uint256 owe + ); + // --- Init --- - constructor(address vat_, address spotter_, address dog_, bytes32 ilk_, address oracle_) public - Clipper(vat_, spotter_, dog_, ilk_) - { + constructor(address vat_, bytes32 ilk_, address oracle_) public + { + vat = VatLike(vat_); + ilk = ilk_; oracle = oracle_; + + wards[msg.sender] = 1; + emit Rely(msg.sender); } // --- Administration --- - function file(bytes32 what, uint256 data) public override auth lock { - if (what == "bee") { - bee = data; - File(what, data); - } - else { - locked = 0; - super.file(what, data); - } + function file(bytes32 what, uint256 data) external auth { + if (what == "bee") bee = data; + else revert("Blipper/file-unrecognized-param"); + + emit File(what, data); } - function file(bytes32 what, address data) public override auth lock { - if (what == "bprotocol") { - bprotocol = data; - File(what, data); - } - else if(what == "oracle") { - oracle = data; - File(what, data); - } - else { - locked = 0; - super.file(what, data); - } + function file(bytes32 what, address data) external auth { + if (what == "bprotocol") bprotocol = data; + else if(what == "oracle") oracle = data; + else if(what == "clipper") clipper = data; + + else revert("Blipper/file-unrecognized-param"); + + emit File(what, data); } + // --- Math --- + uint256 constant BLN = 10 ** 9; + uint256 constant WAD = 10 ** 18; + uint256 constant RAY = 10 ** 27; + + function min(uint256 x, uint256 y) internal pure returns (uint256 z) { + z = x <= y ? x : y; + } + function add(uint256 x, uint256 y) internal pure returns (uint256 z) { + require((z = x + y) >= x); + } + function sub(uint256 x, uint256 y) internal pure returns (uint256 z) { + require((z = x - y) <= x); + } + function mul(uint256 x, uint256 y) internal pure returns (uint256 z) { + require(y == 0 || (z = x * y) / y == x); + } + function wmul(uint256 x, uint256 y) internal pure returns (uint256 z) { + z = mul(x, y) / WAD; + } + function rmul(uint256 x, uint256 y) internal pure returns (uint256 z) { + z = mul(x, y) / RAY; + } + function rdiv(uint256 x, uint256 y) internal pure returns (uint256 z) { + z = mul(x, RAY) / y; + } // get the price directly from the OSM // Could get this from rmul(Vat.ilks(ilk).spot, Spotter.mat()) instead, but @@ -110,12 +191,22 @@ contract Blipper is Clipper { uint256 lot, // Collateral [wad] address usr, // Address that will receive any leftover collateral address kpr // Address that will receive incentives - ) public auth lock isStopped(1) override returns (uint256 id) { - try this.blink(lot, tab, usr, kpr) returns(uint256 /*amt*/, uint256 /*owe*/) { - // TODO - emit events + ) public auth returns (uint256 id) { + require(ClipperLike(clipper).stopped() < 1, "Blipper/stopped-incorrect"); + + try this.blink(lot, tab, usr, kpr) returns(uint256 amt, uint256 owe) { + emit Blip(tab, lot, usr, kpr, amt, owe); + return 0; } catch { - locked = 0; - return super.kick(tab, lot, usr, kpr); + return ClipperLike(clipper).kick(tab, lot, usr, kpr); } } + + // Public function to update the cached clipper params. + function upparams() public { + dog = ClipperLike(clipper).dog(); + vow = ClipperLike(clipper).vow(); + chip = ClipperLike(clipper).chip(); + tip = ClipperLike(clipper).tip(); + } } diff --git a/src/blip.t.sol b/src/blip.t.sol index 7aea153..aac51b0 100644 --- a/src/blip.t.sol +++ b/src/blip.t.sol @@ -1,12 +1,13 @@ pragma solidity >=0.6.12; -import { DssDeployTestBase, Vat } from "dss-deploy/DssDeploy.t.base.sol"; +import { DssDeployTestBase, Vat, Clipper } from "dss-deploy/DssDeploy.t.base.sol"; import { Blipper } from "./blip.sol"; import { BAMMJoin } from "./bammJoin.sol"; contract BammJoinTest is DssDeployTestBase { BAMMJoin bamm; Blipper blipper; + Clipper clipper; function setUp() override public { super.setUp(); @@ -17,19 +18,25 @@ contract BammJoinTest is DssDeployTestBase { assertEq(uint(1), dog.wards(address(this))); - blipper = new Blipper(address(vat), address(spotter), address(dog), "ETH", address(pipETH)); + clipper = new Clipper(address(vat), address(spotter), address(dog), "ETH"); + + blipper = new Blipper(address(vat), "ETH", address(pipETH)); dog.file("ETH", "clip", address(blipper)); dog.file("Hole", 10000000e45); dog.file("ETH", "hole", 10000000e45); dog.file("ETH", "chop", 113e16); - blipper.file("vow", address(vow)); - blipper.file("buf", 2e27); - blipper.file("tip", 1e27); + clipper.file("vow", address(vow)); + clipper.file("buf", 2e27); + clipper.file("tip", 1e27); + clipper.upchost(); this.rely(address(vat), address(blipper)); + this.rely(address(vat), address(clipper)); this.rely(address(dog), address(blipper)); blipper.rely(address(dog)); + clipper.rely(address(blipper)); + assertEq(uint(1), clipper.wards(address(blipper))); weth.mint(1e30); weth.approve(address(ethJoin), uint(-1)); @@ -43,6 +50,10 @@ contract BammJoinTest is DssDeployTestBase { bamm = new BAMMJoin(address(vat), address(spotter), address(pipETH), "ETH", address(blipper), address(pot), address(0xfee), 400); blipper.file("bprotocol", address(bamm)); blipper.file("bee", 105e25); /* 5% premium */ + blipper.file("clipper", address(clipper)); + blipper.upparams(); + + assertEq(blipper.clipper(), address(clipper)); vat.suck(address(0x5), address(this), 1000000 ether * 1e27); vat.hope(address(bamm)); @@ -104,9 +115,9 @@ contract BammJoinTest is DssDeployTestBase { spotter.poke("ETH"); pipETH.poke(bytes32(uint(9 * 1e18))); - uint kickBefore = blipper.kicks(); + uint kickBefore = clipper.kicks(); dog.bark("ETH", address(this), address(0x123)); - uint kickAfter = blipper.kicks(); + uint kickAfter = clipper.kicks(); assertEq(kickBefore + 1, kickAfter, "testBarkWithClipper: expected auction to start"); assertEq(vat.dai(address(0x123)), 1e27); From 5187e15814df83853715ea7e02199945929694fa Mon Sep 17 00:00:00 2001 From: yaron velner Date: Tue, 30 Nov 2021 11:53:58 +0200 Subject: [PATCH 3/5] remove files --- src/org/clip.sol | 473 ----------------------------------------------- src/org/pot.sol | 166 ----------------- 2 files changed, 639 deletions(-) delete mode 100644 src/org/clip.sol delete mode 100644 src/org/pot.sol diff --git a/src/org/clip.sol b/src/org/clip.sol deleted file mode 100644 index 9541b31..0000000 --- a/src/org/clip.sol +++ /dev/null @@ -1,473 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-or-later - -/// clip.sol -- Dai auction module 2.0 - -// Copyright (C) 2020-2021 Maker Ecosystem Growth Holdings, INC. -// -// 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 . - -pragma solidity >=0.6.12; - -interface VatLike { - function move(address,address,uint256) external; - function flux(bytes32,address,address,uint256) external; - function ilks(bytes32) external returns (uint256, uint256, uint256, uint256, uint256); - function suck(address,address,uint256) external; -} - -interface PipLike { - function peek() external returns (bytes32, bool); -} - -interface SpotterLike { - function par() external returns (uint256); - function ilks(bytes32) external returns (PipLike, uint256); -} - -interface DogLike { - function chop(bytes32) external returns (uint256); - function digs(bytes32, uint256) external; -} - -interface ClipperCallee { - function clipperCall(address, uint256, uint256, bytes calldata) external; -} - -interface AbacusLike { - function price(uint256, uint256) external view returns (uint256); -} - -contract Clipper { - // --- Auth --- - mapping (address => uint256) public wards; - function rely(address usr) external auth { wards[usr] = 1; emit Rely(usr); } - function deny(address usr) external auth { wards[usr] = 0; emit Deny(usr); } - modifier auth { - require(wards[msg.sender] == 1, "Clipper/not-authorized"); - _; - } - - // --- Data --- - bytes32 immutable public ilk; // Collateral type of this Clipper - VatLike immutable public vat; // Core CDP Engine - - DogLike public dog; // Liquidation module - address public vow; // Recipient of dai raised in auctions - SpotterLike public spotter; // Collateral price module - AbacusLike public calc; // Current price calculator - - uint256 public buf; // Multiplicative factor to increase starting price [ray] - uint256 public tail; // Time elapsed before auction reset [seconds] - uint256 public cusp; // Percentage drop before auction reset [ray] - uint64 public chip; // Percentage of tab to suck from vow to incentivize keepers [wad] - uint192 public tip; // Flat fee to suck from vow to incentivize keepers [rad] - uint256 public chost; // Cache the ilk dust times the ilk chop to prevent excessive SLOADs [rad] - - uint256 public kicks; // Total auctions - uint256[] public active; // Array of active auction ids - - struct Sale { - uint256 pos; // Index in active array - uint256 tab; // Dai to raise [rad] - uint256 lot; // collateral to sell [wad] - address usr; // Liquidated CDP - uint96 tic; // Auction start time - uint256 top; // Starting price [ray] - } - mapping(uint256 => Sale) public sales; - - uint256 internal locked; - - // Levels for circuit breaker - // 0: no breaker - // 1: no new kick() - // 2: no new kick() or redo() - // 3: no new kick(), redo(), or take() - uint256 public stopped = 0; - - // --- Events --- - event Rely(address indexed usr); - event Deny(address indexed usr); - - event File(bytes32 indexed what, uint256 data); - event File(bytes32 indexed what, address data); - - event Kick( - uint256 indexed id, - uint256 top, - uint256 tab, - uint256 lot, - address indexed usr, - address indexed kpr, - uint256 coin - ); - event Take( - uint256 indexed id, - uint256 max, - uint256 price, - uint256 owe, - uint256 tab, - uint256 lot, - address indexed usr - ); - event Redo( - uint256 indexed id, - uint256 top, - uint256 tab, - uint256 lot, - address indexed usr, - address indexed kpr, - uint256 coin - ); - - event Yank(uint256 id); - - // --- Init --- - constructor(address vat_, address spotter_, address dog_, bytes32 ilk_) public { - vat = VatLike(vat_); - spotter = SpotterLike(spotter_); - dog = DogLike(dog_); - ilk = ilk_; - buf = RAY; - wards[msg.sender] = 1; - emit Rely(msg.sender); - } - - // --- Synchronization --- - modifier lock { - require(locked == 0, "Clipper/system-locked"); - locked = 1; - _; - locked = 0; - } - - modifier isStopped(uint256 level) { - require(stopped < level, "Clipper/stopped-incorrect"); - _; - } - - // --- Administration --- - function file(bytes32 what, uint256 data) public virtual auth lock { - if (what == "buf") buf = data; - else if (what == "tail") tail = data; // Time elapsed before auction reset - else if (what == "cusp") cusp = data; // Percentage drop before auction reset - else if (what == "chip") chip = uint64(data); // Percentage of tab to incentivize (max: 2^64 - 1 => 18.xxx WAD = 18xx%) - else if (what == "tip") tip = uint192(data); // Flat fee to incentivize keepers (max: 2^192 - 1 => 6.277T RAD) - else if (what == "stopped") stopped = data; // Set breaker (0, 1, 2, or 3) - else revert("Clipper/file-unrecognized-param"); - emit File(what, data); - } - function file(bytes32 what, address data) public virtual auth lock { - if (what == "spotter") spotter = SpotterLike(data); - else if (what == "dog") dog = DogLike(data); - else if (what == "vow") vow = data; - else if (what == "calc") calc = AbacusLike(data); - else revert("Clipper/file-unrecognized-param"); - emit File(what, data); - } - - // --- Math --- - uint256 constant BLN = 10 ** 9; - uint256 constant WAD = 10 ** 18; - uint256 constant RAY = 10 ** 27; - - function min(uint256 x, uint256 y) internal pure returns (uint256 z) { - z = x <= y ? x : y; - } - function add(uint256 x, uint256 y) internal pure returns (uint256 z) { - require((z = x + y) >= x); - } - function sub(uint256 x, uint256 y) internal pure returns (uint256 z) { - require((z = x - y) <= x); - } - function mul(uint256 x, uint256 y) internal pure returns (uint256 z) { - require(y == 0 || (z = x * y) / y == x); - } - function wmul(uint256 x, uint256 y) internal pure returns (uint256 z) { - z = mul(x, y) / WAD; - } - function rmul(uint256 x, uint256 y) internal pure returns (uint256 z) { - z = mul(x, y) / RAY; - } - function rdiv(uint256 x, uint256 y) internal pure returns (uint256 z) { - z = mul(x, RAY) / y; - } - - // --- Auction --- - - // get the price directly from the OSM - // Could get this from rmul(Vat.ilks(ilk).spot, Spotter.mat()) instead, but - // if mat has changed since the last poke, the resulting value will be - // incorrect. - function getFeedPrice() internal returns (uint256 feedPrice) { - (PipLike pip, ) = spotter.ilks(ilk); - (bytes32 val, bool has) = pip.peek(); - require(has, "Clipper/invalid-price"); - feedPrice = rdiv(mul(uint256(val), BLN), spotter.par()); - } - - // start an auction - // note: trusts the caller to transfer collateral to the contract - // The starting price `top` is obtained as follows: - // - // top = val * buf / par - // - // Where `val` is the collateral's unitary value in USD, `buf` is a - // multiplicative factor to increase the starting price, and `par` is a - // reference per DAI. - function kick( - uint256 tab, // Debt [rad] - uint256 lot, // Collateral [wad] - address usr, // Address that will receive any leftover collateral - address kpr // Address that will receive incentives - ) public auth lock virtual isStopped(1) returns (uint256 id) { - // Input validation - require(tab > 0, "Clipper/zero-tab"); - require(lot > 0, "Clipper/zero-lot"); - require(usr != address(0), "Clipper/zero-usr"); - id = ++kicks; - require(id > 0, "Clipper/overflow"); - - active.push(id); - - sales[id].pos = active.length - 1; - - sales[id].tab = tab; - sales[id].lot = lot; - sales[id].usr = usr; - sales[id].tic = uint96(block.timestamp); - - uint256 top; - top = rmul(getFeedPrice(), buf); - require(top > 0, "Clipper/zero-top-price"); - sales[id].top = top; - - // incentive to kick auction - uint256 _tip = tip; - uint256 _chip = chip; - uint256 coin; - if (_tip > 0 || _chip > 0) { - coin = add(_tip, wmul(tab, _chip)); - vat.suck(vow, kpr, coin); - } - - emit Kick(id, top, tab, lot, usr, kpr, coin); - } - - // Reset an auction - // See `kick` above for an explanation of the computation of `top`. - function redo( - uint256 id, // id of the auction to reset - address kpr // Address that will receive incentives - ) external lock isStopped(2) { - // Read auction data - address usr = sales[id].usr; - uint96 tic = sales[id].tic; - uint256 top = sales[id].top; - - require(usr != address(0), "Clipper/not-running-auction"); - - // Check that auction needs reset - // and compute current price [ray] - (bool done,) = status(tic, top); - require(done, "Clipper/cannot-reset"); - - uint256 tab = sales[id].tab; - uint256 lot = sales[id].lot; - sales[id].tic = uint96(block.timestamp); - - uint256 feedPrice = getFeedPrice(); - top = rmul(feedPrice, buf); - require(top > 0, "Clipper/zero-top-price"); - sales[id].top = top; - - // incentive to redo auction - uint256 _tip = tip; - uint256 _chip = chip; - uint256 coin; - if (_tip > 0 || _chip > 0) { - uint256 _chost = chost; - if (tab >= _chost && mul(lot, feedPrice) >= _chost) { - coin = add(_tip, wmul(tab, _chip)); - vat.suck(vow, kpr, coin); - } - } - - emit Redo(id, top, tab, lot, usr, kpr, coin); - } - - // Buy up to `amt` of collateral from the auction indexed by `id`. - // - // Auctions will not collect more DAI than their assigned DAI target,`tab`; - // thus, if `amt` would cost more DAI than `tab` at the current price, the - // amount of collateral purchased will instead be just enough to collect `tab` DAI. - // - // To avoid partial purchases resulting in very small leftover auctions that will - // never be cleared, any partial purchase must leave at least `Clipper.chost` - // remaining DAI target. `chost` is an asynchronously updated value equal to - // (Vat.dust * Dog.chop(ilk) / WAD) where the values are understood to be determined - // by whatever they were when Clipper.upchost() was last called. Purchase amounts - // will be minimally decreased when necessary to respect this limit; i.e., if the - // specified `amt` would leave `tab < chost` but `tab > 0`, the amount actually - // purchased will be such that `tab == chost`. - // - // If `tab <= chost`, partial purchases are no longer possible; that is, the remaining - // collateral can only be purchased entirely, or not at all. - function take( - uint256 id, // Auction id - uint256 amt, // Upper limit on amount of collateral to buy [wad] - uint256 max, // Maximum acceptable price (DAI / collateral) [ray] - address who, // Receiver of collateral and external call address - bytes calldata data // Data to pass in external call; if length 0, no call is done - ) external lock isStopped(3) { - - address usr = sales[id].usr; - uint96 tic = sales[id].tic; - - require(usr != address(0), "Clipper/not-running-auction"); - - uint256 price; - { - bool done; - (done, price) = status(tic, sales[id].top); - - // Check that auction doesn't need reset - require(!done, "Clipper/needs-reset"); - } - - // Ensure price is acceptable to buyer - require(max >= price, "Clipper/too-expensive"); - - uint256 lot = sales[id].lot; - uint256 tab = sales[id].tab; - uint256 owe; - - { - // Purchase as much as possible, up to amt - uint256 slice = min(lot, amt); // slice <= lot - - // DAI needed to buy a slice of this sale - owe = mul(slice, price); - - // Don't collect more than tab of DAI - if (owe > tab) { - // Total debt will be paid - owe = tab; // owe' <= owe - // Adjust slice - slice = owe / price; // slice' = owe' / price <= owe / price == slice <= lot - } else if (owe < tab && slice < lot) { - // If slice == lot => auction completed => dust doesn't matter - uint256 _chost = chost; - if (tab - owe < _chost) { // safe as owe < tab - // If tab <= chost, buyers have to take the entire lot. - require(tab > _chost, "Clipper/no-partial-purchase"); - // Adjust amount to pay - owe = tab - _chost; // owe' <= owe - // Adjust slice - slice = owe / price; // slice' = owe' / price < owe / price == slice < lot - } - } - - // Calculate remaining tab after operation - tab = tab - owe; // safe since owe <= tab - // Calculate remaining lot after operation - lot = lot - slice; - - // Send collateral to who - vat.flux(ilk, address(this), who, slice); - - // Do external call (if data is defined) but to be - // extremely careful we don't allow to do it to the two - // contracts which the Clipper needs to be authorized - DogLike dog_ = dog; - if (data.length > 0 && who != address(vat) && who != address(dog_)) { - ClipperCallee(who).clipperCall(msg.sender, owe, slice, data); - } - - // Get DAI from caller - vat.move(msg.sender, vow, owe); - - // Removes Dai out for liquidation from accumulator - dog_.digs(ilk, lot == 0 ? tab + owe : owe); - } - - if (lot == 0) { - _remove(id); - } else if (tab == 0) { - vat.flux(ilk, address(this), usr, lot); - _remove(id); - } else { - sales[id].tab = tab; - sales[id].lot = lot; - } - - emit Take(id, max, price, owe, tab, lot, usr); - } - - function _remove(uint256 id) internal { - uint256 _move = active[active.length - 1]; - if (id != _move) { - uint256 _index = sales[id].pos; - active[_index] = _move; - sales[_move].pos = _index; - } - active.pop(); - delete sales[id]; - } - - // The number of active auctions - function count() external view returns (uint256) { - return active.length; - } - - // Return the entire array of active auctions - function list() external view returns (uint256[] memory) { - return active; - } - - // Externally returns boolean for if an auction needs a redo and also the current price - function getStatus(uint256 id) external view returns (bool needsRedo, uint256 price, uint256 lot, uint256 tab) { - // Read auction data - address usr = sales[id].usr; - uint96 tic = sales[id].tic; - - bool done; - (done, price) = status(tic, sales[id].top); - - needsRedo = usr != address(0) && done; - lot = sales[id].lot; - tab = sales[id].tab; - } - - // Internally returns boolean for if an auction needs a redo - function status(uint96 tic, uint256 top) internal view returns (bool done, uint256 price) { - price = calc.price(top, sub(block.timestamp, tic)); - done = (sub(block.timestamp, tic) > tail || rdiv(price, top) < cusp); - } - - // Public function to update the cached dust*chop value. - function upchost() external { - (,,,, uint256 _dust) = VatLike(vat).ilks(ilk); - chost = wmul(_dust, dog.chop(ilk)); - } - - // Cancel an auction during ES or via governance action. - function yank(uint256 id) external auth lock { - require(sales[id].usr != address(0), "Clipper/not-running-auction"); - dog.digs(ilk, sales[id].tab); - vat.flux(ilk, address(this), msg.sender, sales[id].lot); - _remove(id); - emit Yank(id); - } -} diff --git a/src/org/pot.sol b/src/org/pot.sol deleted file mode 100644 index 13ddce0..0000000 --- a/src/org/pot.sol +++ /dev/null @@ -1,166 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-or-later - -/// pot.sol -- Dai Savings Rate - -// Copyright (C) 2018 Rain -// -// 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 . - -pragma solidity >=0.5.12; - -// FIXME: This contract was altered compared to the production version. -// It doesn't use LibNote anymore. -// New deployments of this contract will need to include custom events (TO DO). - -/* - "Savings Dai" is obtained when Dai is deposited into - this contract. Each "Savings Dai" accrues Dai interest - at the "Dai Savings Rate". - - This contract does not implement a user tradeable token - and is intended to be used with adapters. - - --- `save` your `dai` in the `pot` --- - - - `dsr`: the Dai Savings Rate - - `pie`: user balance of Savings Dai - - - `join`: start saving some dai - - `exit`: remove some dai - - `drip`: perform rate collection - -*/ - -interface VatLike { - function move(address,address,uint256) external; - function suck(address,address,uint256) external; -} - -contract Pot { - // --- Auth --- - mapping (address => uint) public wards; - function rely(address guy) external auth { wards[guy] = 1; } - function deny(address guy) external auth { wards[guy] = 0; } - modifier auth { - require(wards[msg.sender] == 1, "Pot/not-authorized"); - _; - } - - // --- Data --- - mapping (address => uint256) public pie; // Normalised Savings Dai [wad] - - uint256 public Pie; // Total Normalised Savings Dai [wad] - uint256 public dsr; // The Dai Savings Rate [ray] - uint256 public chi; // The Rate Accumulator [ray] - - VatLike public vat; // CDP Engine - address public vow; // Debt Engine - uint256 public rho; // Time of last drip [unix epoch time] - - uint256 public live; // Active Flag - - // --- Init --- - constructor(address vat_) public { - wards[msg.sender] = 1; - vat = VatLike(vat_); - dsr = ONE; - chi = ONE; - rho = now; - live = 1; - } - - // --- Math --- - uint256 constant ONE = 10 ** 27; - function rpow(uint x, uint n, uint base) internal pure returns (uint z) { - assembly { - switch x case 0 {switch n case 0 {z := base} default {z := 0}} - default { - switch mod(n, 2) case 0 { z := base } default { z := x } - let half := div(base, 2) // for rounding. - for { n := div(n, 2) } n { n := div(n,2) } { - let xx := mul(x, x) - if iszero(eq(div(xx, x), x)) { revert(0,0) } - let xxRound := add(xx, half) - if lt(xxRound, xx) { revert(0,0) } - x := div(xxRound, base) - if mod(n,2) { - let zx := mul(z, x) - if and(iszero(iszero(x)), iszero(eq(div(zx, x), z))) { revert(0,0) } - let zxRound := add(zx, half) - if lt(zxRound, zx) { revert(0,0) } - z := div(zxRound, base) - } - } - } - } - } - - function rmul(uint x, uint y) internal pure returns (uint z) { - z = mul(x, y) / ONE; - } - - function add(uint x, uint y) internal pure returns (uint z) { - require((z = x + y) >= x); - } - - function sub(uint x, uint y) internal pure returns (uint z) { - require((z = x - y) <= x); - } - - function mul(uint x, uint y) internal pure returns (uint z) { - require(y == 0 || (z = x * y) / y == x); - } - - // --- Administration --- - function file(bytes32 what, uint256 data) external auth { - require(live == 1, "Pot/not-live"); - require(now == rho, "Pot/rho-not-updated"); - if (what == "dsr") dsr = data; - else revert("Pot/file-unrecognized-param"); - } - - function file(bytes32 what, address addr) external auth { - if (what == "vow") vow = addr; - else revert("Pot/file-unrecognized-param"); - } - - function cage() external auth { - live = 0; - dsr = ONE; - } - - // --- Savings Rate Accumulation --- - function drip() external returns (uint tmp) { - require(now >= rho, "Pot/invalid-now"); - tmp = rmul(rpow(dsr, now - rho, ONE), chi); - uint chi_ = sub(tmp, chi); - chi = tmp; - rho = now; - vat.suck(address(vow), address(this), mul(Pie, chi_)); - } - - // --- Savings Dai Management --- - function join(uint wad) external { - require(now == rho, "Pot/rho-not-updated"); - pie[msg.sender] = add(pie[msg.sender], wad); - Pie = add(Pie, wad); - vat.move(msg.sender, address(this), mul(chi, wad)); - } - - function exit(uint wad) external { - pie[msg.sender] = sub(pie[msg.sender], wad); - Pie = sub(Pie, wad); - vat.move(address(this), msg.sender, mul(chi, wad)); - } -} From 83c49041db9e9b085de423883d0bc0cf8fb79556 Mon Sep 17 00:00:00 2001 From: yaron velner Date: Tue, 30 Nov 2021 12:17:15 +0200 Subject: [PATCH 4/5] bid calculation - mitigate overflow --- src/blip.sol | 2 +- src/blip.t.sol | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/blip.sol b/src/blip.sol index 2605a2e..6d0526f 100644 --- a/src/blip.sol +++ b/src/blip.sol @@ -153,7 +153,7 @@ contract Blipper { uint256 ask = rmul(tab, WAD) / rdiv(mid, bee); // how much dai to get for the entire collateral - uint256 bid = rdiv(mul(wmul(lot, mid), RAY), bee); + uint256 bid = mul(mul(lot, BLN), rdiv(mid, bee)); if(ask <= lot) { amt = ask; diff --git a/src/blip.t.sol b/src/blip.t.sol index aac51b0..a1eeecb 100644 --- a/src/blip.t.sol +++ b/src/blip.t.sol @@ -102,8 +102,8 @@ contract BammJoinTest is DssDeployTestBase { assertEq(vat.dai(address(0x123)), 1e27); // 100 ether to dai with 5% premium - uint daiDebt = 1e27 * uint(100 ether * 110 * 100) / 105; - assertEq(vat.dai(address(vow)), daiDebt); + uint daiDebt = 100 * (uint(100 ether * 110) / 105); + assertEq(vat.dai(address(vow)), daiDebt * 1e27); assertEq(dog.Dirt(), 0); (,,,uint dirt) = dog.ilks("ETH"); assertEq(dirt, 0); From 3012227e7dcfb28116f18d1b3a9e5f9d4781e0ba Mon Sep 17 00:00:00 2001 From: yaron velner Date: Sun, 5 Dec 2021 10:52:52 +0200 Subject: [PATCH 5/5] send lot to clipper --- src/blip.sol | 1 + src/blip.t.sol | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/blip.sol b/src/blip.sol index 6d0526f..fb2a8a0 100644 --- a/src/blip.sol +++ b/src/blip.sol @@ -198,6 +198,7 @@ contract Blipper { emit Blip(tab, lot, usr, kpr, amt, owe); return 0; } catch { + vat.flux(ilk, address(this), clipper, lot); return ClipperLike(clipper).kick(tab, lot, usr, kpr); } } diff --git a/src/blip.t.sol b/src/blip.t.sol index 4469081..900eb55 100644 --- a/src/blip.t.sol +++ b/src/blip.t.sol @@ -149,6 +149,8 @@ contract BammJoinTest is DssDeployTestBase { assertEq(dog.Dirt(), daiDebt * 1e27); (,,,uint dirt) = dog.ilks("ETH"); assertEq(dirt, daiDebt * 1e27); + + assertEq(vat.gem("ETH", address(clipper)), 100 ether); } }