From ea2d72dcba117edda775e71c254ea4d5a50e81bb Mon Sep 17 00:00:00 2001 From: Sam MacPherson Date: Fri, 10 Dec 2021 11:06:24 -0500 Subject: [PATCH 1/2] changed vow to use a proxy --- src/test/clip.t.sol | 9 +++-- src/test/end.t.sol | 15 +++++--- src/test/vat.t.sol | 15 +++++--- src/test/vow.t.sol | 9 ++++- src/vow.sol | 94 +++++++++++++++++++++++++++++++++++---------- 5 files changed, 107 insertions(+), 35 deletions(-) diff --git a/src/test/clip.t.sol b/src/test/clip.t.sol index db31b4ec..33f37424 100644 --- a/src/test/clip.t.sol +++ b/src/test/clip.t.sol @@ -8,7 +8,7 @@ import "ds-value/value.sol"; import {Vat} from "../vat.sol"; import {Spotter} from "../spot.sol"; -import {Vow} from "../vow.sol"; +import {Vow, VowProxy} from "../vow.sol"; import {GemJoin, DaiJoin} from "../join.sol"; import {Clipper} from "../clip.sol"; @@ -339,7 +339,10 @@ contract ClipperTest is DSTest { spot = new Spotter(address(vat)); vat.rely(address(spot)); - vow = new Vow(address(vat), address(0), address(0)); + vow = Vow(address(new VowProxy())); + address vowImp = address(new Vow(address(vat))); + VowProxy(address(vow)).setImplementation(vowImp); + vow.init(); gold = new DSToken("GLD"); goldJoin = new GemJoin(address(vat), ilk, address(gold)); vat.rely(address(goldJoin)); @@ -357,7 +360,7 @@ contract ClipperTest is DSTest { dog = new Dog(address(vat)); dog.file("vow", address(vow)); vat.rely(address(dog)); - vow.rely(address(dog)); + VowProxy(address(vow)).rely(address(dog)); vat.init(ilk); diff --git a/src/test/end.t.sol b/src/test/end.t.sol index f574e208..f064976c 100644 --- a/src/test/end.t.sol +++ b/src/test/end.t.sol @@ -27,7 +27,7 @@ import "ds-value/value.sol"; import {Vat} from '../vat.sol'; import {Cat} from '../cat.sol'; import {Dog} from '../dog.sol'; -import {Vow} from '../vow.sol'; +import {Vow, VowProxy} from '../vow.sol'; import {Pot} from '../pot.sol'; import {Flipper} from '../flip.sol'; import {Clipper} from '../clip.sol'; @@ -208,7 +208,12 @@ contract EndTest is DSTest { flop = new Flopper(address(vat), address(gov)); gov.setOwner(address(flop)); - vow = new Vow(address(vat), address(flap), address(flop)); + vow = Vow(address(new VowProxy())); + address vowImp = address(new Vow(address(vat))); + VowProxy(address(vow)).setImplementation(vowImp); + vow.init(); + vow.file("flapper", address(flap)); + vow.file("flopper", address(flop)); pot = new Pot(address(vat)); vat.rely(address(pot)); @@ -217,12 +222,12 @@ contract EndTest is DSTest { cat = new Cat(address(vat)); cat.file("vow", address(vow)); vat.rely(address(cat)); - vow.rely(address(cat)); + VowProxy(address(vow)).rely(address(cat)); dog = new Dog(address(vat)); dog.file("vow", address(vow)); vat.rely(address(dog)); - vow.rely(address(dog)); + VowProxy(address(vow)).rely(address(dog)); spot = new Spotter(address(vat)); vat.file("Line", rad(1_000_000 ether)); @@ -237,7 +242,7 @@ contract EndTest is DSTest { end.file("spot", address(spot)); end.file("wait", 1 hours); vat.rely(address(end)); - vow.rely(address(end)); + VowProxy(address(vow)).rely(address(end)); spot.rely(address(end)); pot.rely(address(end)); cat.rely(address(end)); diff --git a/src/test/vat.t.sol b/src/test/vat.t.sol index f5d65c91..bb21cf27 100644 --- a/src/test/vat.t.sol +++ b/src/test/vat.t.sol @@ -7,7 +7,7 @@ import "ds-token/token.sol"; import {Vat} from '../vat.sol'; import {Cat} from '../cat.sol'; -import {Vow} from '../vow.sol'; +import {Vow, VowProxy} from '../vow.sol'; import {Jug} from '../jug.sol'; import {GemJoin, DaiJoin} from '../join.sol'; @@ -30,8 +30,8 @@ contract TestVat is Vat { } contract TestVow is Vow { - constructor(address vat, address flapper, address flopper) - public Vow(vat, flapper, flopper) {} + constructor(address vat) + public Vow(vat) {} // Total deficit function Awe() public view returns (uint) { return vat.sin(address(this)); @@ -484,7 +484,12 @@ contract BiteTest is DSTest { flap = new Flapper(address(vat), address(gov)); flop = new Flopper(address(vat), address(gov)); - vow = new TestVow(address(vat), address(flap), address(flop)); + vow = TestVow(address(new VowProxy())); + address vowImp = address(new TestVow(address(vat))); + VowProxy(address(vow)).setImplementation(vowImp); + vow.init(); + vow.file("flapper", address(flap)); + vow.file("flopper", address(flop)); flap.rely(address(vow)); flop.rely(address(vow)); @@ -497,7 +502,7 @@ contract BiteTest is DSTest { cat.file("vow", address(vow)); cat.file("box", rad((10 ether) * MLN)); vat.rely(address(cat)); - vow.rely(address(cat)); + VowProxy(address(vow)).rely(address(cat)); gold = new DSToken("GEM"); gold.mint(1000 ether); diff --git a/src/test/vow.t.sol b/src/test/vow.t.sol index 33e4cb87..e19cdf8f 100644 --- a/src/test/vow.t.sol +++ b/src/test/vow.t.sol @@ -7,7 +7,7 @@ import "ds-test/test.sol"; import {Flopper as Flop} from './flop.t.sol'; import {Flapper as Flap} from './flap.t.sol'; import {TestVat as Vat} from './vat.t.sol'; -import {Vow} from '../vow.sol'; +import {Vow, VowProxy} from '../vow.sol'; interface Hevm { function warp(uint256) external; @@ -39,7 +39,12 @@ contract VowTest is DSTest { flop = new Flop(address(vat), address(gov)); flap = new Flap(address(vat), address(gov)); - vow = new Vow(address(vat), address(flap), address(flop)); + vow = Vow(address(new VowProxy())); + address vowImp = address(new Vow(address(vat))); + VowProxy(address(vow)).setImplementation(vowImp); + vow.init(); + vow.file("flapper", address(flap)); + vow.file("flopper", address(flop)); flap.rely(address(vow)); flop.rely(address(vow)); diff --git a/src/vow.sol b/src/vow.sol index d34a502c..67fc2db2 100644 --- a/src/vow.sol +++ b/src/vow.sol @@ -43,21 +43,67 @@ interface VatLike { function nope(address) external; } -contract Vow { - // --- Auth --- - mapping (address => uint) public wards; - function rely(address usr) external auth { require(live == 1, "Vow/not-live"); wards[usr] = 1; } - function deny(address usr) external auth { wards[usr] = 0; } +contract VowProxy { + address public implementation; + uint256 public live; // Active Flag + mapping (address => uint256) public wards; + + event Rely(address indexed usr); + event Deny(address indexed usr); + event SetImplementation(address indexed); + + constructor() public { + wards[msg.sender] = 1; + emit Rely(msg.sender); + live = 1; + } + + function rely(address usr) external auth { + require(live == 1, "Vow/not-live"); + 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, "Vow/not-authorized"); _; } - // --- Data --- - VatLike public vat; // CDP Engine - FlapLike public flapper; // Surplus Auction House - FlopLike public flopper; // Debt Auction House + function setImplementation(address implementation_) external auth { + implementation = implementation_; + emit SetImplementation(implementation_); + } + + fallback() external { + address _impl = implementation; + require(_impl != address(0)); + assembly { + let ptr := mload(0x40) + calldatacopy(ptr, 0, calldatasize()) + let result := delegatecall(gas(), _impl, ptr, calldatasize(), 0, 0) + let size := returndatasize() + returndatacopy(ptr, 0, size) + + switch result + case 0 { revert(ptr, size) } + default { return(ptr, size) } + } + } +} + +contract Vow { + // --- Proxy Storage --- + bytes32 slot0; // avoid collision with proxy's implementation field + uint256 public live; // Active Flag + mapping (address => uint256) public wards; + + // --- Implementation Storage --- mapping (uint256 => uint256) public sin; // debt queue uint256 public Sin; // Queued debt [rad] uint256 public Ash; // On-auction debt [rad] @@ -69,16 +115,24 @@ contract Vow { uint256 public bump; // Flap fixed lot size [rad] uint256 public hump; // Surplus buffer [rad] - uint256 public live; // Active Flag + VatLike public immutable vat; // CDP Engine + FlapLike public flapper; // Surplus Auction House + FlopLike public flopper; // Debt Auction House + + // --- Auth --- + modifier auth { + require(wards[msg.sender] == 1, "Vow/not-authorized"); + _; + } // --- Init --- - constructor(address vat_, address flapper_, address flopper_) public { - wards[msg.sender] = 1; - vat = VatLike(vat_); - flapper = FlapLike(flapper_); - flopper = FlopLike(flopper_); - vat.hope(flapper_); - live = 1; + constructor(address vat_) public { + vat = VatLike(vat_); + } + + // Needs to be called outside of constructor via proxy + function init() external { + vat.hope(address(flapper)); } // --- Math --- @@ -104,7 +158,7 @@ contract Vow { function file(bytes32 what, address data) external auth { if (what == "flapper") { - vat.nope(address(flapper)); + if (address(flapper) != address(0)) vat.nope(address(flapper)); flapper = FlapLike(data); vat.hope(data); } @@ -114,12 +168,12 @@ contract Vow { // Push to debt-queue function fess(uint tab) external auth { - sin[now] = add(sin[now], tab); + sin[block.timestamp] = add(sin[block.timestamp], tab); Sin = add(Sin, tab); } // Pop from debt-queue function flog(uint era) external { - require(add(era, wait) <= now, "Vow/wait-not-finished"); + require(add(era, wait) <= block.timestamp, "Vow/wait-not-finished"); Sin = sub(Sin, sin[era]); sin[era] = 0; } From e1146020bc5bac79397c20b6578e0344348067e0 Mon Sep 17 00:00:00 2001 From: Sam MacPherson Date: Fri, 10 Dec 2021 11:22:11 -0500 Subject: [PATCH 2/2] remove init as it is handled in the file --- src/test/clip.t.sol | 1 - src/test/end.t.sol | 1 - src/test/vat.t.sol | 1 - src/test/vow.t.sol | 1 - src/vow.sol | 5 ----- 5 files changed, 9 deletions(-) diff --git a/src/test/clip.t.sol b/src/test/clip.t.sol index 33f37424..854a1588 100644 --- a/src/test/clip.t.sol +++ b/src/test/clip.t.sol @@ -342,7 +342,6 @@ contract ClipperTest is DSTest { vow = Vow(address(new VowProxy())); address vowImp = address(new Vow(address(vat))); VowProxy(address(vow)).setImplementation(vowImp); - vow.init(); gold = new DSToken("GLD"); goldJoin = new GemJoin(address(vat), ilk, address(gold)); vat.rely(address(goldJoin)); diff --git a/src/test/end.t.sol b/src/test/end.t.sol index f064976c..00897a00 100644 --- a/src/test/end.t.sol +++ b/src/test/end.t.sol @@ -211,7 +211,6 @@ contract EndTest is DSTest { vow = Vow(address(new VowProxy())); address vowImp = address(new Vow(address(vat))); VowProxy(address(vow)).setImplementation(vowImp); - vow.init(); vow.file("flapper", address(flap)); vow.file("flopper", address(flop)); diff --git a/src/test/vat.t.sol b/src/test/vat.t.sol index bb21cf27..171dc597 100644 --- a/src/test/vat.t.sol +++ b/src/test/vat.t.sol @@ -487,7 +487,6 @@ contract BiteTest is DSTest { vow = TestVow(address(new VowProxy())); address vowImp = address(new TestVow(address(vat))); VowProxy(address(vow)).setImplementation(vowImp); - vow.init(); vow.file("flapper", address(flap)); vow.file("flopper", address(flop)); flap.rely(address(vow)); diff --git a/src/test/vow.t.sol b/src/test/vow.t.sol index e19cdf8f..93c8b7c9 100644 --- a/src/test/vow.t.sol +++ b/src/test/vow.t.sol @@ -42,7 +42,6 @@ contract VowTest is DSTest { vow = Vow(address(new VowProxy())); address vowImp = address(new Vow(address(vat))); VowProxy(address(vow)).setImplementation(vowImp); - vow.init(); vow.file("flapper", address(flap)); vow.file("flopper", address(flop)); flap.rely(address(vow)); diff --git a/src/vow.sol b/src/vow.sol index 67fc2db2..e8441de7 100644 --- a/src/vow.sol +++ b/src/vow.sol @@ -130,11 +130,6 @@ contract Vow { vat = VatLike(vat_); } - // Needs to be called outside of constructor via proxy - function init() external { - vat.hope(address(flapper)); - } - // --- Math --- function add(uint x, uint y) internal pure returns (uint z) { require((z = x + y) >= x);