contract RwaUrn {
    // --- auth ---
    mapping (address => uint256) public wards;
    mapping (address => uint256) public can;
    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, "RwaUrn/not-authorized");
        _;
    }
    function hope(address usr) external auth {
        can[usr] = 1;
        emit Hope(usr);
    }
    function nope(address usr) external auth {
        can[usr] = 0;
        emit Nope(usr);
    }
    modifier operator {
        require(can[msg.sender] == 1, "RwaUrn/not-operator");
        _;
    }

    VatAbstract  public vat;
    JugAbstract  public jug;
    GemJoinAbstract public gemJoin;
    DaiJoinAbstract public daiJoin;
    address public outputConduit;

    // Events
    event Rely(address indexed usr);
    event Deny(address indexed usr);
    event Hope(address indexed usr);
    event Nope(address indexed usr);
    event File(bytes32 indexed what, address data);
    event Lock(address indexed usr, uint256 wad);
    event Free(address indexed usr, uint256 wad);
    event Draw(address indexed usr, uint256 wad);
    event Wipe(address indexed usr, uint256 wad);
    event Quit(address indexed usr, uint256 wad);

    // --- math ---
    uint256 constant RAY = 10 ** 27;
    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 divup(uint256 x, uint256 y) internal pure returns (uint256 z) {
        z = add(x, sub(y, 1)) / y;
    }

    // --- init ---
    constructor(
        address vat_, address jug_, address gemJoin_, address daiJoin_, address outputConduit_
    ) public {
        // requires in urn that outputConduit isn't address(0)
        vat = VatAbstract(vat_);
        jug = JugAbstract(jug_);
        gemJoin = GemJoinAbstract(gemJoin_);
        daiJoin = DaiJoinAbstract(daiJoin_);
        outputConduit = outputConduit_;
        wards[msg.sender] = 1;
        DSTokenAbstract(gemJoin.gem()).approve(address(gemJoin), uint256(-1));
        DaiAbstract(daiJoin.dai()).approve(address(daiJoin), uint256(-1));
        VatAbstract(vat_).hope(address(daiJoin));
        emit Rely(msg.sender);
        emit File("outputConduit", outputConduit_);
        emit File("jug", jug_);
    }

    // --- administration ---
    function file(bytes32 what, address data) external auth {
        if (what == "outputConduit") { outputConduit = data; }
        else if (what == "jug") { jug = JugAbstract(data); }
        else revert("RwaUrn/unrecognised-param");
        emit File(what, data);
    }

    // --- cdp operation ---
    // n.b. that the operator must bring the gem
    function lock(uint256 wad) external operator {
        require(wad <= 2**255 - 1, "RwaUrn/overflow");
        DSTokenAbstract(gemJoin.gem()).transferFrom(msg.sender, address(this), wad);
        // join with address this
        gemJoin.join(address(this), wad);
        vat.frob(gemJoin.ilk(), address(this), address(this), address(this), int(wad), 0);
        emit Lock(msg.sender, wad);
    }
    // n.b. that the operator takes the gem
    // and might not be the same operator who brought the gem
    function free(uint256 wad) external operator {
        require(wad <= 2**255, "RwaUrn/overflow");
        vat.frob(gemJoin.ilk(), address(this), address(this), address(this), -int(wad), 0);
        gemJoin.exit(msg.sender, wad);
        emit Free(msg.sender, wad);
    }
    // n.b. DAI can only go to the output conduit
    function draw(uint256 wad) external operator {
        require(outputConduit != address(0));
        bytes32 ilk = gemJoin.ilk();
        jug.drip(ilk);
        (,uint256 rate,,,) = vat.ilks(ilk);
        uint256 dart = divup(mul(RAY, wad), rate);
        require(dart <= 2**255 - 1, "RwaUrn/overflow");
        vat.frob(ilk, address(this), address(this), address(this), 0, int(dart));
        daiJoin.exit(outputConduit, wad);
        emit Draw(msg.sender, wad);
    }
    // n.b. anyone can wipe
    function wipe(uint256 wad) external {
        daiJoin.join(address(this), wad);
        bytes32 ilk = gemJoin.ilk();
        jug.drip(ilk);
        (,uint256 rate,,,) = vat.ilks(ilk);
        uint256 dart = mul(RAY, wad) / rate;
        require(dart <= 2 ** 255, "RwaUrn/overflow");
        vat.frob(ilk, address(this), address(this), address(this), 0, -int(dart));
        emit Wipe(msg.sender, wad);
    }

    // If Dai is sitting here after ES that should be sent back
    function quit() external {
        require(outputConduit != address(0));
        require(vat.live() == 0, "RwaUrn/vat-still-live");
        DSTokenAbstract dai = DSTokenAbstract(daiJoin.dai());
        uint256 wad = dai.balanceOf(address(this));
        dai.transfer(outputConduit, wad);
        emit Quit(msg.sender, wad);
    }
}
function wipe(uint256 wad) external {
    daiJoin.join(address(this), wad);
    bytes32 ilk = gemJoin.ilk();
    jug.drip(ilk);
    (,uint256 rate,,,) = vat.ilks(ilk);
    uint256 dart = mul(RAY, wad) / rate;
    require(dart <= 2 ** 255, "RwaUrn/overflow");
    vat.frob(ilk, address(this), address(this), address(this), 0, -int(dart));
    emit Wipe(msg.sender, wad);
}

// n.b. anyone can wipe