Skip to content
Open

V2 #3

Show file tree
Hide file tree
Changes from all 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
3 changes: 0 additions & 3 deletions .env.example

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,26 @@ contract AuctionMainnetDeployerScript is Script {

address gov = 0x926dF14a23BE491164dCF93f4c468A50ef659D5B;
address fedChair = 0x8F97cCA30Dbe80e7a8B462F1dD1a51C32accDfC8;
address dola = 0x865377367054516e17014CcdED1e7d814EDC9ce4;
address asset = 0x865377367054516e17014CcdED1e7d814EDC9ce4;
address dbr = 0xAD038Eb671c44b853887A7E32528FaB35dC5D710;

// 5:1 ratio, implying a 20c DBR starting price
uint dolaReserve = 500_000 * 1e18;
uint dbrReserve = dolaReserve * 5;
uint assetReserve = 500_000 * 1e18;
uint dbrReserve = assetReserve * 5;

Auction auction = new Auction(
gov,
fedChair,
dbr,
dola,
asset,
handler,
dolaReserve,
assetReserve,
dbrReserve
);

new Helper(
address(auction),
address(dola)
address(asset)
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,27 @@
pragma solidity 0.8.21;

import {Script, console2} from "forge-std/Script.sol";
import {SaleHandler} from "../src/SaleHandler.sol";
import {DolaSaleHandler} from "../src/DolaSaleHandler.sol";
import {Auction} from "../src/Auction.sol";
import {Helper} from "../src/Helper.sol";

contract HandlerMainnetDeployerScript is Script {
contract DolaSaleHandlerMainnetDeployerScript is Script {
function setUp() public {}

function run() public {
vm.startBroadcast();

address gov = 0x926dF14a23BE491164dCF93f4c468A50ef659D5B;
address beneficiary = 0x9D5Df30F475CEA915b1ed4C0CCa59255C897b61B;
address anDola = 0x7Fcb7DAC61eE35b3D4a51117A7c58D53f0a8a670;
address dola = 0x865377367054516e17014CcdED1e7d814EDC9ce4;
address asset = 0x865377367054516e17014CcdED1e7d814EDC9ce4;
address borrower1 = 0xf508c58ce37ce40a40997C715075172691F92e2D;
address borrower2 = 0xeA0c959BBb7476DDD6cD4204bDee82b790AA1562;

// 30% minimum goes to repayments, 70% goes to beneficiary
uint minRepayBps = 3000;

new SaleHandler(
gov,
beneficiary,
minRepayBps,
dola,
DolaSaleHandler handler = new DolaSaleHandler(
asset,
anDola,
borrower1,
borrower2
);

}
}
Empty file removed sh/HandlerMainnetDeployer.sh
Empty file.
92 changes: 59 additions & 33 deletions src/Auction.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,36 +21,37 @@ contract Auction {
address public gov;
address public operator;
IDBR public immutable dbr;
IERC20 public immutable dola;
IERC20 public immutable asset;
ISaleHandler public saleHandler;
uint public dolaReserve;
uint public assetReserve;
uint public dbrReserve;
uint public dbrRatePerYear;
uint public maxDbrRatePerYear;
uint public minDbrRatePerYear;
uint public maxDbrRatePerYear = type(uint).max;
uint public lastUpdate;

constructor (
address _gov,
address _operator,
address _dbr,
address _dola,
address _asset,
address handler,
uint _dolaReserve,
uint _assetReserve,
uint _dbrReserve
) {
require(_dolaReserve > 0, "Dola reserve must be positive");
require(_assetReserve > 0, "Asset reserve must be positive");
require(_dbrReserve > 0, "DBR reserve must be positive");
gov = _gov;
operator = _operator;
dbr = IDBR(_dbr);
dola = IERC20(_dola);
asset = IERC20(_asset);
saleHandler = ISaleHandler(handler);
dolaReserve = _dolaReserve;
assetReserve = _assetReserve;
dbrReserve = _dbrReserve;
}

modifier updateReserves {
(dolaReserve, dbrReserve) = getCurrentReserves();
(assetReserve, dbrReserve) = getCurrentReserves();
lastUpdate = block.timestamp;
_;
}
Expand All @@ -65,15 +66,15 @@ contract Auction {
_;
}

function getCurrentReserves() public view returns (uint _dolaReserve, uint _dbrReserve) {
function getCurrentReserves() public view returns (uint _assetReserve, uint _dbrReserve) {
uint timeElapsed = block.timestamp - lastUpdate;
if(timeElapsed > 0) {
uint K = dolaReserve * dbrReserve;
uint K = assetReserve * dbrReserve;
uint DbrsIn = timeElapsed * dbrRatePerYear / 365 days;
_dbrReserve = dbrReserve + DbrsIn;
_dolaReserve = K / _dbrReserve;
_assetReserve = K / _dbrReserve;
} else {
_dolaReserve = dolaReserve;
_assetReserve = assetReserve;
_dbrReserve = dbrReserve;
}
}
Expand All @@ -83,6 +84,7 @@ contract Auction {
function setSaleHandler(address _saleHandler) external onlyGov { saleHandler = ISaleHandler(_saleHandler); }

function setMaxDbrRatePerYear(uint _maxRate) external onlyGov updateReserves {
require(_maxRate >= minDbrRatePerYear, "Max below min");
maxDbrRatePerYear = _maxRate;
emit MaxRateUpdate(_maxRate);
if(dbrRatePerYear > _maxRate) {
Expand All @@ -91,59 +93,83 @@ contract Auction {
}
}

function setMinDbrRatePerYear(uint _minRate) external onlyGov updateReserves {
require(_minRate <= maxDbrRatePerYear, "Min above max");
minDbrRatePerYear = _minRate;
emit MinRateUpdate(_minRate);
if(dbrRatePerYear < _minRate) {
dbrRatePerYear = _minRate;
emit RateUpdate(_minRate);
}
}

function setDbrRatePerYear(uint _rate) external onlyGovOrOperator updateReserves {
require(_rate <= maxDbrRatePerYear, "Rate exceeds max");
require(_rate >= minDbrRatePerYear, "Rate below min");
dbrRatePerYear = _rate;
emit RateUpdate(_rate);
}

function setDolaReserve(uint _dolaReserve) external onlyGov updateReserves {
require(_dolaReserve > 0, "Dola reserve must be positive");
uint K = dolaReserve * dbrReserve;
dolaReserve = _dolaReserve;
dbrReserve = K / _dolaReserve;
// changes K to preserve the ratio (price)
function setAssetReserve(uint _assetReserve) external onlyGov updateReserves {
require(_assetReserve > 0, "Asset reserve must be positive");
uint newDbrReserve = _assetReserve * dbrReserve / assetReserve;
require(newDbrReserve > 0, "Resulting DBR reserve must be positive");
assetReserve = _assetReserve;
dbrReserve = newDbrReserve;
}

// changes K to preserve the ratio (price)
function setDbrReserve(uint _dbrReserve) external onlyGov updateReserves {
require(_dbrReserve > 0, "DBR reserve must be positive");
uint K = dolaReserve * dbrReserve;
uint newAssetReserve = _dbrReserve * assetReserve / dbrReserve;
require(newAssetReserve > 0, "Resulting asset reserve must be positive");
dbrReserve = _dbrReserve;
dolaReserve = K / _dbrReserve;
assetReserve = newAssetReserve;
}

function overrideReserves(uint _dbrReserve, uint _dolaReserve) external onlyGov {
require(_dolaReserve > 0, "Dola reserve must be positive");
function overrideReserves(uint _dbrReserve, uint _assetReserve) external onlyGov {
require(_assetReserve > 0, "Asset reserve must be positive");
require(_dbrReserve > 0, "DBR reserve must be positive");
dolaReserve = _dolaReserve;
assetReserve = _assetReserve;
dbrReserve = _dbrReserve;
lastUpdate = block.timestamp;
}

function buyDBR(uint exactDolaIn, uint exactDbrOut, address to) external updateReserves {
uint K = dolaReserve * dbrReserve;
dolaReserve += exactDolaIn;
function buyDBR(uint exactAssetIn, uint exactDbrOut, address to) external updateReserves {
uint K = assetReserve * dbrReserve;
assetReserve += exactAssetIn;
dbrReserve -= exactDbrOut;
require(dolaReserve * dbrReserve >= K, "Invariant");
dola.transferFrom(msg.sender, address(this), exactDolaIn);
require(assetReserve * dbrReserve >= K, "Invariant");
asset.transferFrom(msg.sender, address(this), exactAssetIn);
dbr.mint(to, exactDbrOut);
emit Buy(msg.sender, to, exactDolaIn, exactDbrOut);
emit Buy(msg.sender, to, exactAssetIn, exactDbrOut);
}

function sendToSaleHandler() public {
require(address(saleHandler) != address(0), "No sale handler");
uint bal = dola.balanceOf(address(this));
require(bal > 0, "No DOLA to send");
uint bal = asset.balanceOf(address(this));
require(bal > 0, "No asset to send");
uint capacity = saleHandler.getCapacity();
uint amount = bal > capacity ? capacity : bal;
dola.transfer(address(saleHandler), amount);
asset.transfer(address(saleHandler), amount);
saleHandler.onReceive();
}

// only if no sale handler is set
function sendToGov() public {
require(address(saleHandler) == address(0), "Sale handler set");
uint bal = asset.balanceOf(address(this));
require(bal > 0, "No asset to send");
asset.transfer(gov, bal);
}

function sweep(address token, address destination, uint amount) external onlyGov {
IERC20(token).transfer(destination, amount);
}

event Buy(address indexed caller, address indexed to, uint dolaIn, uint dbrOut);
event Buy(address indexed caller, address indexed to, uint assetIn, uint dbrOut);
event RateUpdate(uint newRate);
event MaxRateUpdate(uint newMaxRate);
event MinRateUpdate(uint newMinRate);
}
55 changes: 55 additions & 0 deletions src/DolaSaleHandler.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.21;

interface IERC20 {
function approve(address,uint) external returns (bool);
function balanceOf(address) external view returns (uint);
}

interface IAnDola {
function repayBorrowBehalf(address borrower, uint repayAmount) external returns (uint);
function borrowBalanceStored(address account) external view returns (uint); // stored is good enough for our use case
}

contract DolaSaleHandler {

IERC20 public immutable asset;
IAnDola public immutable anDola;
address public immutable borrower1;
address public immutable borrower2;

constructor(
address _asset,
address _anDola,
address _borrower1,
address _borrower2
) {
asset = IERC20(_asset);
anDola = IAnDola(_anDola);
borrower1 = _borrower1;
borrower2 = _borrower2;
asset.approve(_anDola,type(uint).max);
}

function onReceive() external {
uint bal = asset.balanceOf(address(this));
uint debt1 = getDebtOf(borrower1);
uint debt2 = getDebtOf(borrower2);
if(debt1 > debt2) {
uint errCode = anDola.repayBorrowBehalf(borrower1, bal);
if(errCode > 0) require(anDola.repayBorrowBehalf(borrower2, bal) == 0, "Failed to repay");
} else {
uint errCode = anDola.repayBorrowBehalf(borrower2, bal);
if(errCode > 0) require(anDola.repayBorrowBehalf(borrower1, bal) == 0, "Failed to repay");
}
}

function getDebtOf(address borrower) internal view returns (uint) {
return anDola.borrowBalanceStored(borrower);
}

function getCapacity() external view returns (uint) {
return getDebtOf(borrower1) + getDebtOf(borrower2) - asset.balanceOf(address(this));
}

}
48 changes: 24 additions & 24 deletions src/Helper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
pragma solidity 0.8.21;

interface IAuction {
function getCurrentReserves() external view returns (uint dolaReserve, uint dbrReserve);
function buyDBR(uint exactDolaIn, uint exactDbrOut, address to) external;
function getCurrentReserves() external view returns (uint assetReserve, uint dbrReserve);
function buyDBR(uint exactAssetIn, uint exactDbrOut, address to) external;
}

interface IERC20 {
Expand All @@ -14,45 +14,45 @@ interface IERC20 {
contract Helper {

IAuction public immutable auction;
IERC20 public immutable dola;
IERC20 public immutable asset;

constructor(
address _auction,
address _dola
address _asset
) {
auction = IAuction(_auction);
dola = IERC20(_dola);
dola.approve(_auction, type(uint).max);
asset = IERC20(_asset);
asset.approve(_auction, type(uint).max);
}

function getDbrOut(uint dolaIn) public view returns (uint dbrOut) {
require(dolaIn > 0, "dolaIn must be positive");
(uint dolaReserve, uint dbrReserve) = auction.getCurrentReserves();
uint numerator = dolaIn * dbrReserve;
uint denominator = dolaReserve + dolaIn;
function getDbrOut(uint assetIn) public view returns (uint dbrOut) {
require(assetIn > 0, "assetIn must be positive");
(uint assetReserve, uint dbrReserve) = auction.getCurrentReserves();
uint numerator = assetIn * dbrReserve;
uint denominator = assetReserve + assetIn;
dbrOut = numerator / denominator;
}

function getDolaIn(uint dbrOut) public view returns (uint dolaIn) {
function getAssetIn(uint dbrOut) public view returns (uint assetIn) {
require(dbrOut > 0, "dbrOut must be positive");
(uint dolaReserve, uint dbrReserve) = auction.getCurrentReserves();
uint numerator = dbrOut * dolaReserve;
(uint assetReserve, uint dbrReserve) = auction.getCurrentReserves();
uint numerator = dbrOut * assetReserve;
uint denominator = dbrReserve - dbrOut;
dolaIn = (numerator / denominator) + 1;
assetIn = (numerator / denominator) + 1;
}

function swapExactDolaForDbr(uint dolaIn, uint dbrOutMin) external returns (uint dbrOut) {
dbrOut = getDbrOut(dolaIn);
function swapExactAssetForDbr(uint assetIn, uint dbrOutMin) external returns (uint dbrOut) {
dbrOut = getDbrOut(assetIn);
require(dbrOut >= dbrOutMin, "dbrOut must be greater than dbrOutMin");
dola.transferFrom(msg.sender, address(this), dolaIn);
auction.buyDBR(dolaIn, dbrOut, msg.sender);
asset.transferFrom(msg.sender, address(this), assetIn);
auction.buyDBR(assetIn, dbrOut, msg.sender);
}

function swapDolaForExactDbr(uint dbrOut, uint dolaInMax) external returns (uint dolaIn) {
dolaIn = getDolaIn(dbrOut);
require(dolaIn <= dolaInMax, "dolaIn must be less than dolaInMax");
dola.transferFrom(msg.sender, address(this), dolaIn);
auction.buyDBR(dolaIn, dbrOut, msg.sender);
function swapAssetForExactDbr(uint dbrOut, uint assetInMax) external returns (uint assetIn) {
assetIn = getAssetIn(dbrOut);
require(assetIn <= assetInMax, "assetIn must be less than assetInMax");
asset.transferFrom(msg.sender, address(this), assetIn);
auction.buyDBR(assetIn, dbrOut, msg.sender);
}

}
Loading