// SPDX-License-Identifier: AGPL-3.0-or-later /// vat.sol -- Dai CDP database // 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.6.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). contract Vat { // --- Auth --- mapping (address => uint) public wards; function rely(address usr) external auth { require(live == 1, "Vat/not-live"); wards[usr] = 1; } function deny(address usr) external auth { require(live == 1, "Vat/not-live"); wards[usr] = 0; } modifier auth { require(wards[msg.sender] == 1, "Vat/not-authorized"); _; } mapping(address => mapping (address => uint)) public can; function hope(address usr) external { can[msg.sender][usr] = 1; } function nope(address usr) external { can[msg.sender][usr] = 0; } function wish(address bit, address usr) internal view returns (bool) { return either(bit == usr, can[bit][usr] == 1); } // --- Data --- struct Ilk { uint256 Art; // Total Normalised Debt [wad] uint256 rate; // Accumulated Rates [ray] uint256 spot; // Price with Safety Margin [ray] uint256 line; // Debt Ceiling [rad] uint256 dust; // Urn Debt Floor [rad] } struct Urn { uint256 ink; // Locked Collateral [wad] uint256 art; // Normalised Debt [wad] } mapping (bytes32 => Ilk) public ilks; mapping (bytes32 => mapping (address => Urn )) public urns; mapping (bytes32 => mapping (address => uint)) public gem; // [wad] mapping (address => uint256) public dai; // [rad] mapping (address => uint256) public sin; // [rad] uint256 public debt; // Total Dai Issued [rad] uint256 public vice; // Total Unbacked Dai [rad] uint256 public Line; // Total Debt Ceiling [rad] uint256 public live; // Active Flag // --- Init --- constructor() public { wards[msg.sender] = 1; live = 1; } // --- Math --- function _add(uint x, int y) internal pure returns (uint z) { z = x + uint(y); require(y >= 0 || z <= x); require(y <= 0 || z >= x); } function _sub(uint x, int y) internal pure returns (uint z) { z = x - uint(y); require(y <= 0 || z <= x); require(y >= 0 || z >= x); } function _mul(uint x, int y) internal pure returns (int z) { z = int(x) * y; require(int(x) >= 0); require(y == 0 || z / y == int(x)); } 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 init(bytes32 ilk) external auth { require(ilks[ilk].rate == 0, "Vat/ilk-already-init"); ilks[ilk].rate = 10 ** 27; } function file(bytes32 what, uint data) external auth { require(live == 1, "Vat/not-live"); if (what == "Line") Line = data; else revert("Vat/file-unrecognized-param"); } function file(bytes32 ilk, bytes32 what, uint data) external auth { require(live == 1, "Vat/not-live"); if (what == "spot") ilks[ilk].spot = data; else if (what == "line") ilks[ilk].line = data; else if (what == "dust") ilks[ilk].dust = data; else revert("Vat/file-unrecognized-param"); } function cage() external auth { live = 0; } // --- Fungibility --- function slip(bytes32 ilk, address usr, int256 wad) external auth { gem[ilk][usr] = _add(gem[ilk][usr], wad); } function flux(bytes32 ilk, address src, address dst, uint256 wad) external { require(wish(src, msg.sender), "Vat/not-allowed"); gem[ilk][src] = _sub(gem[ilk][src], wad); gem[ilk][dst] = _add(gem[ilk][dst], wad); } function move(address src, address dst, uint256 rad) external { require(wish(src, msg.sender), "Vat/not-allowed"); dai[src] = _sub(dai[src], rad); dai[dst] = _add(dai[dst], rad); } function either(bool x, bool y) internal pure returns (bool z) { assembly{ z := or(x, y)} } function both(bool x, bool y) internal pure returns (bool z) { assembly{ z := and(x, y)} } // --- CDP Manipulation --- function frob(bytes32 i, address u, address v, address w, int dink, int dart) external { // system is live require(live == 1, "Vat/not-live"); Urn memory urn = urns[i][u]; Ilk memory ilk = ilks[i]; // ilk has been initialised require(ilk.rate != 0, "Vat/ilk-not-init"); urn.ink = _add(urn.ink, dink); urn.art = _add(urn.art, dart); ilk.Art = _add(ilk.Art, dart); int dtab = _mul(ilk.rate, dart); uint tab = _mul(ilk.rate, urn.art); debt = _add(debt, dtab); // either debt has decreased, or debt ceilings are not exceeded require(either(dart <= 0, both(_mul(ilk.Art, ilk.rate) <= ilk.line, debt <= Line)), "Vat/ceiling-exceeded"); // urn is either less risky than before, or it is safe require(either(both(dart <= 0, dink >= 0), tab <= _mul(urn.ink, ilk.spot)), "Vat/not-safe"); // urn is either more safe, or the owner consents require(either(both(dart <= 0, dink >= 0), wish(u, msg.sender)), "Vat/not-allowed-u"); // collateral src consents require(either(dink <= 0, wish(v, msg.sender)), "Vat/not-allowed-v"); // debt dst consents require(either(dart >= 0, wish(w, msg.sender)), "Vat/not-allowed-w"); // urn has no debt, or a non-dusty amount require(either(urn.art == 0, tab >= ilk.dust), "Vat/dust"); gem[i][v] = _sub(gem[i][v], dink); dai[w] = _add(dai[w], dtab); urns[i][u] = urn; ilks[i] = ilk; } // --- CDP Fungibility --- function fork(bytes32 ilk, address src, address dst, int dink, int dart) external { Urn storage u = urns[ilk][src]; Urn storage v = urns[ilk][dst]; Ilk storage i = ilks[ilk]; u.ink = _sub(u.ink, dink); u.art = _sub(u.art, dart); v.ink = _add(v.ink, dink); v.art = _add(v.art, dart); uint utab = _mul(u.art, i.rate); uint vtab = _mul(v.art, i.rate); // both sides consent require(both(wish(src, msg.sender), wish(dst, msg.sender)), "Vat/not-allowed"); // both sides safe require(utab <= _mul(u.ink, i.spot), "Vat/not-safe-src"); require(vtab <= _mul(v.ink, i.spot), "Vat/not-safe-dst"); // both sides non-dusty require(either(utab >= i.dust, u.art == 0), "Vat/dust-src"); require(either(vtab >= i.dust, v.art == 0), "Vat/dust-dst"); } // --- CDP Confiscation --- function grab(bytes32 i, address u, address v, address w, int dink, int dart) external auth { Urn storage urn = urns[i][u]; Ilk storage ilk = ilks[i]; urn.ink = _add(urn.ink, dink); urn.art = _add(urn.art, dart); ilk.Art = _add(ilk.Art, dart); int dtab = _mul(ilk.rate, dart); gem[i][v] = _sub(gem[i][v], dink); sin[w] = _sub(sin[w], dtab); vice = _sub(vice, dtab); } // --- Settlement --- function heal(uint rad) external { address u = msg.sender; sin[u] = _sub(sin[u], rad); dai[u] = _sub(dai[u], rad); vice = _sub(vice, rad); debt = _sub(debt, rad); } function suck(address u, address v, uint rad) external auth { sin[u] = _add(sin[u], rad); dai[v] = _add(dai[v], rad); vice = _add(vice, rad); debt = _add(debt, rad); } // --- Rates --- function fold(bytes32 i, address u, int rate) external auth { require(live == 1, "Vat/not-live"); Ilk storage ilk = ilks[i]; ilk.rate = _add(ilk.rate, rate); int rad = _mul(ilk.Art, rate); dai[u] = _add(dai[u], rad); debt = _add(debt, rad); } }