Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V2 #335

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open

V2 #335

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
7 changes: 6 additions & 1 deletion contracts/interfaces/IBondSDA.sol
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.15;

import {IERC20} from "./IERC20.sol";
import {IERC20} from "./IERC20.v2.sol";

interface IBondSDA {
/// @notice Creates a new bond market
/// @param params_ Configuration data needed for market creation
/// @return id ID of new bond market
function createMarket(bytes calldata params_) external returns (uint256);

/// @notice Disable existing bond market
/// @notice Must be market owner
/// @param id_ ID of market to close
function closeMarket(uint256 id_) external;
}
2 changes: 1 addition & 1 deletion contracts/interfaces/IBondTeller.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.15;

import {IERC20} from "./IERC20.sol";
import {IERC20} from "./IERC20.v2.sol";

interface IBondTeller {
/// @notice Instantiates a new fixed expiry bond token
Expand Down
28 changes: 28 additions & 0 deletions contracts/interfaces/IERC20.v2.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity >=0.8.15;

interface IERC20 {
function totalSupply() external view returns (uint256);

function balanceOf(address account) external view returns (uint256);

function transfer(address recipient, uint256 amount) external returns (bool);

function allowance(address owner, address spender) external view returns (uint256);

function approve(address spender, uint256 amount) external returns (bool);

function increaseAllowance(address spender, uint256 addedValue) external returns (bool);

function decreaseAllowance(address spender, uint256 subtractedValue) external returns (bool);

function transferFrom(
address sender,
address recipient,
uint256 amount
) external returns (bool);

event Transfer(address indexed from, address indexed to, uint256 value);

event Approval(address indexed owner, address indexed spender, uint256 value);
}
6 changes: 5 additions & 1 deletion contracts/interfaces/IEasyAuction.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.15;

import {IERC20} from "./IERC20.sol";
import {IERC20} from "./IERC20.v2.sol";

interface IEasyAuction {
/// @notice Initiates an auction through Gnosis Auctions
Expand Down Expand Up @@ -29,4 +29,8 @@ interface IEasyAuction {
address accessManager,
bytes calldata accessManagerData
) external returns (uint256);

/// @notice Settles an auction and identifies the clearing price
/// @param auctionId The ID of the auction to settle
function settleAuction(uint256 auctionId) external returns (bytes32 clearingOrder);
}
40 changes: 21 additions & 19 deletions contracts/peripheral/OhmBondManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pragma solidity 0.8.15;
import {IBondSDA} from "../interfaces/IBondSDA.sol";
import {IBondTeller} from "../interfaces/IBondTeller.sol";
import {IEasyAuction} from "../interfaces/IEasyAuction.sol";
import {IERC20} from "../interfaces/IERC20.sol";
import {IERC20} from "../interfaces/IERC20.v2.sol";
import {ITreasury} from "../interfaces/ITreasury.sol";
import {IOlympusAuthority} from "../interfaces/IOlympusAuthority.sol";
import {OlympusAccessControlled} from "../types/OlympusAccessControlled.sol";
Expand Down Expand Up @@ -61,9 +61,9 @@ contract OhmBondManager is OlympusAccessControlled {

// ========= MARKET CREATION ========= //
function createBondProtocolMarket(uint256 capacity_, uint256 bondTerm_) external onlyPolicy returns (uint256) {
_topUpOhm(capacity_);
treasury.mint(address(this), capacity_);

/// Encodes the information needed for creating a bond market on Bond Protocol
// Encodes the information needed for creating a bond market on Bond Protocol
bytes memory createMarketParams = abi.encode(
ohm, // payoutToken
ohm, // quoteToken
Expand All @@ -79,23 +79,27 @@ contract OhmBondManager is OlympusAccessControlled {
int8(0) // scaleAdjustment
);

ohm.approve(address(fixedExpiryTeller), capacity_);
ohm.increaseAllowance(address(fixedExpiryTeller), capacity_);
uint256 marketId = fixedExpiryAuctioneer.createMarket(createMarketParams);

return marketId;
}

function closeBondProtocolMarket(uint256 id_) external onlyPolicy {
fixedExpiryAuctioneer.closeMarket(id_);
}

function createGnosisAuction(uint96 capacity_, uint256 bondTerm_) external onlyPolicy returns (uint256) {
_topUpOhm(capacity_);
treasury.mint(address(this), capacity_);

uint48 expiry = uint48(block.timestamp + bondTerm_);

/// Create bond token
ohm.approve(address(fixedExpiryTeller), capacity_);
// Create bond token
ohm.increaseAllowance(address(fixedExpiryTeller), capacity_);
fixedExpiryTeller.deploy(ohm, expiry);
(IERC20 bondToken, ) = fixedExpiryTeller.create(ohm, expiry, capacity_);

/// Launch Gnosis Auction
// Launch Gnosis Auction
bondToken.approve(address(gnosisEasyAuction), capacity_);
uint256 auctionId = gnosisEasyAuction.initiateAuction(
bondToken, // auctioningToken
Expand All @@ -114,6 +118,10 @@ contract OhmBondManager is OlympusAccessControlled {
return auctionId;
}

function settleGnosisAuction(uint256 id_) external onlyPolicy {
gnosisEasyAuction.settleAuction(id_);
}

// ========= PARAMETER ADJUSTMENT ========= //
function setBondProtocolParameters(
uint256 initialPrice_,
Expand Down Expand Up @@ -147,18 +155,12 @@ contract OhmBondManager is OlympusAccessControlled {
});
}

// ========= INTERNAL FUNCTIONS ========= //
function _topUpOhm(uint256 amountToDeploy_) internal {
uint256 ohmBalance = ohm.balanceOf(address(this));

if (amountToDeploy_ > ohmBalance) {
uint256 amountToMint = amountToDeploy_ - ohmBalance;
treasury.mint(address(this), amountToMint);
}
// ========= EMERGENCY FUNCTIONS ========= //
function setEmergencyApproval(address token_, address spender_, uint256 amount_) external onlyPolicy {
IERC20(token_).approve(spender_, amount_);
}

// ========= EMERGENCY FUNCTIONS ========= //
function emergencyWithdraw(uint256 amount) external onlyPolicy {
ohm.transfer(address(treasury), amount);
function emergencyWithdraw(uint256 amount_) external onlyPolicy {
ohm.transfer(address(treasury), amount_);
}
}
104 changes: 103 additions & 1 deletion test/bonds/OhmBondManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ import {
OlympusTreasury,
} from "../../types";
import { addEth, addressZero, bne, getCoin, impersonate, pinBlock } from "../utils/scripts";
import { advanceTime } from "../utils/Utilities";
import { olympus } from "../utils/olympus";
import { coins } from "../utils/coins";
import { BigNumber } from "ethers";
import { easyAuctionAbi, feAuctioneerAbi, feTellerAbi, bondAggregatorAbi } from "../utils/abi";
import { defaultAbiCoder } from "ethers/lib/utils";

// Network
const url: string = config.networks.hardhat.forking!.url;
Expand Down Expand Up @@ -252,6 +252,37 @@ describe.only("OhmBondManager", () => {
});
});

describe("closeBondProtocolMarket", () => {
beforeEach(async () => {
await ohmBondManager.connect(policy).setBondProtocolParameters(
"1000000000000000000000000000000000000", // 1e36
"500000000000000000000000000000000000", // 5e35
100_000,
604800,
21600
);
await ohmBondManager
.connect(policy)
.createBondProtocolMarket("10000000000000", 1210000);
});

it("can only be called by policy", async () => {
const marketId = (await bondAggregator.marketCounter()).sub("1");
await expect(ohmBondManager.connect(policy).closeBondProtocolMarket(marketId)).to.not.be.reverted;

await expect(ohmBondManager.connect(other).closeBondProtocolMarket(marketId)).to.be.reverted;

await expect(ohmBondManager.connect(guardian).closeBondProtocolMarket(marketId)).to.be.reverted;
});

it("should correctly close the market", async () => {
const marketId = (await bondAggregator.marketCounter()).sub("1");
await ohmBondManager.connect(policy).closeBondProtocolMarket(marketId);

expect((await feAuctioneer.isLive(marketId))).to.be.false;
});
});

describe("createGnosisAuction", () => {
beforeEach(async () => {
await ohmBondManager
Expand Down Expand Up @@ -290,6 +321,77 @@ describe.only("OhmBondManager", () => {
});
});

describe("settleGnosisAuction", () => {
beforeEach(async () => {
await ohmBondManager
.connect(policy)
.setGnosisAuctionParameters(518400, 604800, 2, "10000000", "1000000000000");

await ohmBondManager.connect(policy).createGnosisAuction("10000000000000", 1210000);
});

it("can only be called by policy", async () => {
await advanceTime(1210005);

const auctionId = await easyAuction.auctionCounter();
await expect(ohmBondManager.connect(policy).settleGnosisAuction(auctionId)).to.not.be.reverted;

await expect(ohmBondManager.connect(other).settleGnosisAuction(auctionId)).to.be.reverted;

await expect(ohmBondManager.connect(guardian).settleGnosisAuction(auctionId)).to.be.reverted;
});

it("should settle the auction", async () => {
await advanceTime(1210005);

const auctionId = await easyAuction.auctionCounter();
await expect(ohmBondManager.connect(policy).settleGnosisAuction(auctionId)).to.not.be.reverted;

const auctionData = await easyAuction.auctionData(auctionId);
expect(auctionData.minimumBiddingAmountPerOrder).to.equal("0");
});
});

describe("series of market creations", () => {
beforeEach(async () => {
await ohmBondManager.connect(policy).setBondProtocolParameters(
"1000000000000000000000000000000000000", // 1e36
"500000000000000000000000000000000000", // 5e35
100_000,
604800,
21600
);

await ohmBondManager
.connect(policy)
.setGnosisAuctionParameters(518400, 604800, 2, "10000000", "1000000000000");
});

it("should not steal OHM when launching subsequent markets", async () => {
await ohmBondManager.connect(policy).createBondProtocolMarket(10000000000000, 1210000);
await ohmBondManager.connect(policy).createGnosisAuction(10000000000000, 1210000);

expect((await ohm.allowance(ohmBondManager.address, feTeller.address))).to.equal(10000000000000);
expect((await ohm.balanceOf(ohmBondManager.address))).to.equal(10000000000000);
});
});

describe("setEmergencyApproval", () => {
it("can only be called by policy", async () => {
await expect(ohmBondManager.connect(policy).setEmergencyApproval(ohm.address, feTeller.address, "10000000000000")).to.not.be.reverted;

await expect(ohmBondManager.connect(other).setEmergencyApproval(ohm.address, feTeller.address, "10000000000000")).to.be.reverted;

await expect(ohmBondManager.connect(guardian).setEmergencyApproval(ohm.address, feTeller.address, "10000000000000")).to.be.reverted;
});

it("should set approval on the passed token", async () => {
expect((await ohm.allowance(ohmBondManager.address, feTeller.address))).to.equal("0");
await ohmBondManager.connect(policy).setEmergencyApproval(ohm.address, feTeller.address, "10000000000000");
expect((await ohm.allowance(ohmBondManager.address, feTeller.address))).to.equal("10000000000000");
});
});

describe("emergencyWithdraw", () => {
beforeEach(async () => {
ohm.connect(guardian).transfer(ohmBondManager.address, "1000000000000");
Expand Down