Skip to content

Added Mig deployments #90

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
46 changes: 46 additions & 0 deletions archive/deploy/mig/CauldronV3MIG.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { HardhatRuntimeEnvironment } from "hardhat/types";
import { DeployFunction } from "hardhat-deploy/types";
import { network } from "hardhat";
import { ChainId, setDeploymentSupportedChains, wrappedDeploy } from "../utilities";
import { Constants, xMerlin } from "../test/constants";
import { CauldronV3 } from "../typechain/CauldronV3";

const ParametersPerChain = {
[ChainId.Mainnet]: {
deploymentName: "CauldronV3MasterContractMIGMainnet",
degenBox: Constants.mainnet.degenBox,
mim: Constants.mainnet.mig,
owner: xMerlin,
},
};

const deployFunction: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const { getNamedAccounts } = hre;
const { deployer } = await getNamedAccounts();
const chainId = await hre.getChainId();
const parameters = ParametersPerChain[parseInt(chainId)];

// Deploy CauldronV3 MasterContract
const CauldronV3MasterContract = await wrappedDeploy<CauldronV3>(parameters.deploymentName, {
from: deployer,
args: [parameters.degenBox, parameters.mim],
log: true,
contract: "CauldronV3",
deterministicDeployment: false,
});

await (await CauldronV3MasterContract.setFeeTo(parameters.owner)).wait();

if (network.name !== "hardhat") {
if ((await CauldronV3MasterContract.owner()) != parameters.owner) {
await (await CauldronV3MasterContract.transferOwnership(parameters.owner, true, false)).wait();
}
}
};

export default deployFunction;

setDeploymentSupportedChains(Object.keys(ParametersPerChain), deployFunction);

deployFunction.tags = ["CauldronV3MIG"];
deployFunction.dependencies = [];
43 changes: 43 additions & 0 deletions archive/deploy/mig/MIG.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { HardhatRuntimeEnvironment } from "hardhat/types";
import { DeployFunction } from "hardhat-deploy/types";
import { network } from "hardhat";
import { ChainId, setDeploymentSupportedChains, wrappedDeploy } from "../utilities";
import { Constants, xMerlin } from "../test/constants";
import { MagicInternetGold } from "../typechain/MagicInternetGold";

const ParametersPerChain = {
[ChainId.Mainnet]: {
deploymentName: "MagicInternetGold",
owner: xMerlin,
},
};

const deployFunction: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const { getNamedAccounts } = hre;
const { deployer } = await getNamedAccounts();
const chainId = await hre.getChainId();
const parameters = ParametersPerChain[parseInt(chainId)];

// Deploy MIG
const magicInternetGold = await wrappedDeploy<MagicInternetGold>(parameters.deploymentName, {
from: deployer,
args: [],
log: true,
contract: "MagicInternetGold",
deterministicDeployment: false,
});


if (network.name !== "hardhat") {
if ((await magicInternetGold.owner()) != parameters.owner) {
await (await magicInternetGold.transferOwnership(parameters.owner, true, false)).wait();
}
}
};

export default deployFunction;

setDeploymentSupportedChains(Object.keys(ParametersPerChain), deployFunction);

deployFunction.tags = ["MIG"];
deployFunction.dependencies = [];
76 changes: 76 additions & 0 deletions contracts/MagicInternetGold.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// SPDX-License-Identifier: MIT

// Magic Internet Money

// ███╗ ███╗██╗ ██████╗
// ████╗ ████║██║██╔════╝
// ██╔████╔██║██║██║ ███╗
// ██║╚██╔╝██║██║██║ ██║
// ██║ ╚═╝ ██║██║╚██████╔╝
// ╚═╝ ╚═╝╚═╝ ╚═════╝


// BoringCrypto, 0xMerlin

pragma solidity 0.6.12;
import "@boringcrypto/boring-solidity/contracts/libraries/BoringMath.sol";
import "@boringcrypto/boring-solidity/contracts/ERC20.sol";
import "@sushiswap/bentobox-sdk/contracts/IBentoBoxV1.sol";
import "@boringcrypto/boring-solidity/contracts/BoringOwnable.sol";

/// @title Magic Internet Gold
contract MagicInternetGold is ERC20, BoringOwnable {
using BoringMath for uint256;
// ERC20 'variables'
string public constant symbol = "MIG";
string public constant name = "Magic Internet Gold";
uint8 public constant decimals = 18;
uint256 public override totalSupply;

struct Minting {
uint128 time;
uint128 amount;
}

Minting public lastMint;
uint256 private constant MINTING_PERIOD = 24 hours;
uint256 private constant MINTING_INCREASE = 15000;
uint256 private constant MINTING_PRECISION = 1e5;

// @notice mint MIG to an address
// @param to - the recipient
// @param amount - the amount minted
function mint(address to, uint256 amount) public onlyOwner {
require(to != address(0), "MIG: no mint to zero address");

// Limits the amount minted per period to a convergence function, with the period duration restarting on every mint
uint256 totalMintedAmount = uint256(lastMint.time < block.timestamp - MINTING_PERIOD ? 0 : lastMint.amount).add(amount);
require(totalSupply == 0 || totalSupply.mul(MINTING_INCREASE) / MINTING_PRECISION >= totalMintedAmount);

lastMint.time = block.timestamp.to128();
lastMint.amount = totalMintedAmount.to128();

totalSupply = totalSupply + amount;
balanceOf[to] += amount;
emit Transfer(address(0), to, amount);
}

// @notice mint MIG to a recipient on BentoBox
// @param clone - the recipient clone contract
// @param amount - the amount minted
// @param bentoBox - the address of the BentoBox / DegenBox selected
function mintToBentoBox(address clone, uint256 amount, IBentoBoxV1 bentoBox) public onlyOwner {
mint(address(bentoBox), amount);
bentoBox.deposit(IERC20(address(this)), address(bentoBox), clone, amount, 0);
}

// @notice burn MIG from caller
// @param amount - the amount burnt
function burn(uint256 amount) public {
require(amount <= balanceOf[msg.sender], "MIM: not enough");

balanceOf[msg.sender] -= amount;
totalSupply -= amount;
emit Transfer(msg.sender, address(0), amount);
}
}
55 changes: 55 additions & 0 deletions contracts/oracles/WbtcOracleMig.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import "../interfaces/IOracle.sol";

// Chainlink Aggregator

interface IAggregator {
function latestAnswer() external view returns (int256 answer);
}

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

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

contract WbtcOracleMig is IOracle {
IAggregator public constant BTCUSD = IAggregator(0xF4030086522a5bEEa4988F8cA5B36dbC97BeE88c);
IAggregator public constant GOLD = IAggregator(0x214eD9Da11D2fbe465a6fc601a91E62EbEc1a0D6);
// Calculates the lastest exchange rate
// Uses both divide and multiply only for tokens not supported directly by Chainlink, for example MKR/USD
function _get() internal view returns (uint256) {
uint256 btcPrice = uint256(BTCUSD.latestAnswer());
uint256 goldPrice = uint256(GOLD.latestAnswer());
return goldPrice * 1e8 / btcPrice;
}

// Get the latest exchange rate
/// @inheritdoc IOracle
function get(bytes calldata) public view override returns (bool, uint256) {
return (true, _get());
}

// Check the last exchange rate without any state changes
/// @inheritdoc IOracle
function peek(bytes calldata) public view override returns (bool, uint256) {
return (true, _get());
}

// Check the current spot exchange rate without any state changes
/// @inheritdoc IOracle
function peekSpot(bytes calldata data) external view override returns (uint256 rate) {
(, rate) = peek(data);
}

/// @inheritdoc IOracle
function name(bytes calldata) public pure override returns (string memory) {
return "Chainlink GOLD BTC";
}

/// @inheritdoc IOracle
function symbol(bytes calldata) public pure override returns (string memory) {
return "LINK/GOLD/BTC";
}
}
134 changes: 134 additions & 0 deletions deploy/WBTCMIG.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import { HardhatRuntimeEnvironment } from "hardhat/types";
import { DeployFunction } from "hardhat-deploy/types";
import { deployments, ethers, getNamedAccounts } from "hardhat";
import { expect } from "chai";
import { DegenBox, IOracle, ProxyOracle } from "../typechain";
import { ChainId, setDeploymentSupportedChains, wrappedDeploy } from "../utilities";
import { Constants, xMerlin } from "../test/constants";

const INTEREST_CONVERSION = 1e18 / (365.25 * 3600 * 24) / 100;
const OPENING_CONVERSION = 1e5 / 100;

const oracleData = "0x0000000000000000000000000000000000000000";

export const ParametersPerChain = {

[ChainId.Mainnet]: {
enabled: true,
cauldronV3MC: Constants.mainnet.cauldronV3mig,
degenBox: Constants.mainnet.degenBox,
mim: Constants.mainnet.mig,
owner: xMerlin,

collateralization: 85 * 1e3, // 75% LTV
opening: 0.05 * OPENING_CONVERSION, // 0% initial
interest: parseInt(String(0 * INTEREST_CONVERSION)), // 0% Interest
liquidation: 7 * 1e3 + 1e5, // 10% liquidation fee

cauldrons: [
{
deploymentNamePrefix: "WBTCMig",
collateral: Constants.mainnet.wbtc,
},
],
},
};

const deployFunction: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const { deployer } = await getNamedAccounts();
const chainId = parseInt(await hre.getChainId());
const parameters = ParametersPerChain[chainId];

if (!parameters.enabled) {
console.log(`Deployment disabled for chain id ${chainId}`);
return;
}

const getDeployment = async (name: string) => {
try {
return (await deployments.get(name)).address
} catch {
return undefined
}
}

const DegenBox = await ethers.getContractAt<DegenBox>("DegenBox", parameters.degenBox);
const cauldrons = parameters.cauldrons;

for (let i = 0; i < cauldrons.length; i++) {
const cauldron = cauldrons[i];

const ProxyOracle = await wrappedDeploy<ProxyOracle>(`${cauldron.deploymentNamePrefix}ProxyOracle`, {
from: deployer,
args: [],
log: true,
contract: "ProxyOracle",
deterministicDeployment: false,
});

const Oracle = await wrappedDeploy<IOracle>(`${cauldron.deploymentNamePrefix}OracleV1`, {
from: deployer,
args: [],
log: true,
contract: "WbtcOracleMig",
deterministicDeployment: false,
});

if ((await ProxyOracle.oracleImplementation()) !== Oracle.address) {
await (await ProxyOracle.changeOracleImplementation(Oracle.address)).wait();
}
if ((await ProxyOracle.owner()) !== xMerlin) {
await (await ProxyOracle.transferOwnership(xMerlin, true, false)).wait();
}

let initData = ethers.utils.defaultAbiCoder.encode(
["address", "address", "bytes", "uint64", "uint256", "uint256", "uint256"],
[
cauldron.collateral,
ProxyOracle.address,
oracleData,
parameters.interest,
parameters.liquidation,
parameters.collateralization,
parameters.opening,
]
);

const cauldronAddress = await getDeployment(`${cauldron.deploymentNamePrefix}Cauldron`)

if(cauldronAddress === undefined) {
const tx = await (await DegenBox.deploy(parameters.cauldronV3MC, initData, true)).wait();

const deployEvent = tx?.events?.[0];
expect(deployEvent?.eventSignature).to.be.eq("LogDeploy(address,bytes,address)");

deployments.save(`${cauldron.deploymentNamePrefix}Cauldron`, {
abi: [],
address: deployEvent?.args?.cloneAddress,
});
}

/* // Liquidation Swapper
await wrappedDeploy(`${cauldron.deploymentNamePrefix}Swapper`, {
from: deployer,
log: true,
contract: "YVCrvStETHSwapper2",
deterministicDeployment: false,
});

// Leverage Swapper
await wrappedDeploy(`${cauldron.deploymentNamePrefix}LevSwapper`, {
from: deployer,
log: true,
contract: "YVCrvStETHLevSwapper2",
deterministicDeployment: false,
}); */
}
};

export default deployFunction;

setDeploymentSupportedChains(Object.keys(ParametersPerChain), deployFunction);

deployFunction.tags = ["WBTCMig"];
deployFunction.dependencies = [];
Loading