Skip to content

Commit

Permalink
introduce fixed rate mynt conversion
Browse files Browse the repository at this point in the history
  • Loading branch information
cwsnt committed Oct 18, 2023
1 parent 9a1761f commit a43f74b
Show file tree
Hide file tree
Showing 28 changed files with 2,928 additions and 30 deletions.
173 changes: 173 additions & 0 deletions contracts/converter/FixedRateConverter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

interface IMynt {
function burn(address _account, uint256 _amount) external;
}

contract FixedRateConverter {
using SafeERC20 for IERC20;

address public admin;
address public myntContractAddress;
address public sovContractAddress;
uint256 public conversionFixedRate;

event SetAdmin(address indexed sender, address indexed oldAdmin, address indexed newAdmin);
event SetMyntContractAddress(
address indexed sender,
address indexed oldMyntContractAddress,
address indexed newMyntContractAddreess
);
event SetSovContractAddress(
address indexed sender,
address indexed oldSovContractAddress,
address indexed newSovContractAddreess
);
event SetConversionFixedRate(
address indexed sender,
uint256 indexed oldConversionFixedrate,
uint256 indexed newConversionFixedRate
);
event SovWithdrawn(address indexed recipient, uint256 amountWithdrawn);
event Convert(address indexed sender, uint256 myntSent, uint256 sovReceived);

/// @dev TODO: Check for restrictions in this contract.
modifier onlyAdmin() {
require(msg.sender == admin, "unauthorized");
_;
}

constructor(
address _myntContractAddress,
address _sovContractAddress,
uint256 _conversionFixedRate
) {

Check warning on line 48 in contracts/converter/FixedRateConverter.sol

View workflow job for this annotation

GitHub Actions / build

Explicitly mark visibility in function

Check warning on line 48 in contracts/converter/FixedRateConverter.sol

View workflow job for this annotation

GitHub Actions / build

Explicitly mark visibility in function
_setAdmin(msg.sender);
setMyntContractAddress(_myntContractAddress);
setSovContractAddress(_sovContractAddress);
setConversionFixedRate(_conversionFixedRate);
}

/**
* @notice Public function to set admin account.
*
* @param _newAdmin new admin address.
* only admin can perform this action.
*/
function setAdmin(address _newAdmin) public onlyAdmin {
_setAdmin(_newAdmin);
}

/**
* @notice Set the MYNT contract address.
*
* only admin can perform this action.
*
* @param _newMyntContractAddress The new MYNT contract address.
* */
function setMyntContractAddress(address _newMyntContractAddress) public onlyAdmin {
address oldMyntContractAddress = myntContractAddress;

myntContractAddress = _newMyntContractAddress;
emit SetMyntContractAddress(msg.sender, oldMyntContractAddress, myntContractAddress);
}

/**
* @notice Set the sov contract address.
*
* only admin can perform this action.
*
* @param _newSovContractAddress The new sov contract address.
* */
function setSovContractAddress(address _newSovContractAddress) public onlyAdmin {
address oldSovContractAddress = sovContractAddress;

sovContractAddress = _newSovContractAddress;
emit SetSovContractAddress(msg.sender, oldSovContractAddress, sovContractAddress);
}

/**
* @notice Set the conversion fixed rate.
*
* only admin can perform this action.
*
* @param _newConversionFixedRate The new conversion fixed rate.
* */
function setConversionFixedRate(uint256 _newConversionFixedRate) public onlyAdmin {
uint256 oldConversionFixedRate = conversionFixedRate;

conversionFixedRate = _newConversionFixedRate;
emit SetConversionFixedRate(msg.sender, oldConversionFixedRate, conversionFixedRate);
}

/**
* @dev function to convert MYNT to SOV
*
* @param _myntAmount MYNT amount that will be converted.
*/
function convert(uint256 _myntAmount) external {
require(_myntAmount > 0, "Error: amount must be > 0");

uint256 senderMyntBalance = IERC20(myntContractAddress).balanceOf(msg.sender);

Check warning on line 115 in contracts/converter/FixedRateConverter.sol

View workflow job for this annotation

GitHub Actions / build

Explicitly mark all external contracts as trusted or untrusted

Check warning on line 115 in contracts/converter/FixedRateConverter.sol

View workflow job for this annotation

GitHub Actions / build

Explicitly mark all external contracts as trusted or untrusted
require(senderMyntBalance >= _myntAmount, "Error: amount exceeds MYNT balance");

uint256 totalConvertedSov = convertAmount(_myntAmount);

IERC20(myntContractAddress).safeTransferFrom(msg.sender, address(this), _myntAmount);

Check warning on line 120 in contracts/converter/FixedRateConverter.sol

View workflow job for this annotation

GitHub Actions / build

Explicitly mark all external contracts as trusted or untrusted

Check warning on line 120 in contracts/converter/FixedRateConverter.sol

View workflow job for this annotation

GitHub Actions / build

Explicitly mark all external contracts as trusted or untrusted
IERC20(sovContractAddress).safeTransfer(msg.sender, totalConvertedSov);

Check warning on line 121 in contracts/converter/FixedRateConverter.sol

View workflow job for this annotation

GitHub Actions / build

Explicitly mark all external contracts as trusted or untrusted

Check warning on line 121 in contracts/converter/FixedRateConverter.sol

View workflow job for this annotation

GitHub Actions / build

Explicitly mark all external contracts as trusted or untrusted

IMynt(myntContractAddress).burn(address(this), _myntAmount);

Check warning on line 123 in contracts/converter/FixedRateConverter.sol

View workflow job for this annotation

GitHub Actions / build

Explicitly mark all external contracts as trusted or untrusted

Check warning on line 123 in contracts/converter/FixedRateConverter.sol

View workflow job for this annotation

GitHub Actions / build

Explicitly mark all external contracts as trusted or untrusted

emit Convert(msg.sender, _myntAmount, totalConvertedSov);
}

/**
* @dev external function to calculate how many SOV will be converted with the given MYNT amount
*
* @param _myntAmount total MYNT to be converted to SOV.
* @return converted SOV amount.
*/
function convertAmount(uint256 _myntAmount) public view returns (uint256) {
return (_myntAmount * conversionFixedRate) / 1e18;
}

/**
* @dev external function to calculate how many MYNT that can be converted based on the current contract's SOV Balance
*
* @return max amount of MYNT that can be converted.
*/
function convertMax() external view returns (uint256) {
uint256 sovBalance = IERC20(sovContractAddress).balanceOf(address(this));

Check warning on line 144 in contracts/converter/FixedRateConverter.sol

View workflow job for this annotation

GitHub Actions / build

Explicitly mark all external contracts as trusted or untrusted

Check warning on line 144 in contracts/converter/FixedRateConverter.sol

View workflow job for this annotation

GitHub Actions / build

Explicitly mark all external contracts as trusted or untrusted
return (sovBalance * 1e18) / conversionFixedRate;
}

/**
* @dev withdraw the whole SOV balance of this contract
* The whole SOV will be withdrawn to the admin account.
* only admin can perform this action.
*
*/
function withdrawSov() external onlyAdmin {
uint256 sovBalance = IERC20(sovContractAddress).balanceOf(address(this));

Check warning on line 155 in contracts/converter/FixedRateConverter.sol

View workflow job for this annotation

GitHub Actions / build

Explicitly mark all external contracts as trusted or untrusted

Check warning on line 155 in contracts/converter/FixedRateConverter.sol

View workflow job for this annotation

GitHub Actions / build

Explicitly mark all external contracts as trusted or untrusted
address recipient = msg.sender;
IERC20(sovContractAddress).safeTransfer(recipient, sovBalance);

Check warning on line 157 in contracts/converter/FixedRateConverter.sol

View workflow job for this annotation

GitHub Actions / build

Explicitly mark all external contracts as trusted or untrusted

Check warning on line 157 in contracts/converter/FixedRateConverter.sol

View workflow job for this annotation

GitHub Actions / build

Explicitly mark all external contracts as trusted or untrusted

emit SovWithdrawn(recipient, sovBalance);
}

/** Internal function */
/**
* @dev internal function set the admin account.
* @param _newAdmin new admin address
*/
function _setAdmin(address _newAdmin) internal {
require(_newAdmin != address(0), "Invalid address");
address oldAdmin = admin;
admin = _newAdmin;
emit SetAdmin(msg.sender, oldAdmin, admin);
}
}
7 changes: 6 additions & 1 deletion contracts/interfaces/IApproveAndCall.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,10 @@ interface IApproveAndCall {
* @param _token The address of token.
* @param _data The data will be used for low level call.
* */
function receiveApproval(address _sender, uint256 _amount, address _token, bytes calldata _data) external;
function receiveApproval(
address _sender,
uint256 _amount,
address _token,
bytes calldata _data
) external;
}
23 changes: 19 additions & 4 deletions contracts/interfaces/IMassetManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,12 @@ interface IMassetManager {
* @param tokenAddress Address of the bAsset to redeem.
* @param userData Address of the final recipient as ABI encoded bytes.
*/
event onTokensMintedCalled(address indexed sender, uint256 orderAmount, address tokenAddress, bytes userData);
event onTokensMintedCalled(
address indexed sender,
uint256 orderAmount,
address tokenAddress,
bytes userData
);

/***************************************
MINTING (PUBLIC)
Expand All @@ -81,7 +86,10 @@ interface IMassetManager {
* @param _bAssetQuantity Quantity in bAsset units.
* @return massetMinted Quantity of newly minted mAsset.
*/
function mint(address _bAsset, uint256 _bAssetQuantity) external returns (uint256 massetMinted);
function mint(
address _bAsset,
uint256 _bAssetQuantity
) external returns (uint256 massetMinted);

/**
* @dev Mint a single mAsset to recipient address, at a 1:1 ratio with the bAsset.
Expand All @@ -108,7 +116,10 @@ interface IMassetManager {
* @param _massetQuantity Units of the masset to redeem.
* @return massetRedeemed Relative number of mAsset units burned to pay for the bAssets.
*/
function redeem(address _bAsset, uint256 _massetQuantity) external returns (uint256 massetRedeemed);
function redeem(
address _bAsset,
uint256 _massetQuantity
) external returns (uint256 massetRedeemed);

/**
* @dev Credits a recipient with a certain quantity of selected bAsset, in exchange for burning the
Expand Down Expand Up @@ -200,7 +211,11 @@ interface IMassetManager {
* @param _tokenAddress Address of the bAsset to redeem.
* @param _userData Address of the final recipient as ABI encoded bytes.
*/
function onTokensMinted(uint256 _orderAmount, address _tokenAddress, bytes calldata _userData) external;
function onTokensMinted(
uint256 _orderAmount,
address _tokenAddress,
bytes calldata _userData
) external;

// Getters

Expand Down
6 changes: 5 additions & 1 deletion contracts/masset/Token.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ contract Token is ERC20, Ownable {
* @param _symbol The symbol of the token.
* @param _decimalsValue The decimals of the token.
* */
constructor(string memory _name, string memory _symbol, uint8 _decimalsValue) ERC20(_name, _symbol) {
constructor(
string memory _name,
string memory _symbol,
uint8 _decimalsValue
) ERC20(_name, _symbol) {
_decimals = _decimalsValue;
}

Expand Down
42 changes: 42 additions & 0 deletions contracts/mocks/shared/MockMyntToken.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";


contract MockMyntToken is ERC20, Ownable {
uint8 private _decimals;

/**
* @notice Constructor called on deployment, initiates the contract.
* @param _name The name of the token.
* @param _symbol The symbol of the token.
* @param _decimalsValue The decimals of the token.
* */
constructor(string memory _name, string memory _symbol, uint8 _decimalsValue) ERC20(_name, _symbol) {
_decimals = _decimalsValue;
}

function decimals() public view virtual override returns (uint8) {
return _decimals;
}

/**
* @notice Creates new tokens and sends them to the recipient.
* @param _account The recipient address to get the minted tokens.
* @param _amount The amount of tokens to be minted.
* */
function mint(address _account, uint256 _amount) public onlyOwner {
_mint(_account, _amount);
}

/**
* @notice Burns tokens for the given account.
* @param _account The recipient address to get the minted tokens.
* @param _amount The amount of tokens to be minted.
* */
function burn(address _account, uint256 _amount) public {
_burn(_account, _amount);
}
}
26 changes: 26 additions & 0 deletions deployment/deploy/103_DeployFixedRateConverter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { DeployFunction } from "hardhat-deploy/types";

const func: DeployFunction = async ({
deployments: { deploy, get, log },
getNamedAccounts,
}) => {
const { deployer } = await getNamedAccounts();
const deployedMynt = await get("Mynt");
const deployedSov = await get("Sov");
const deployedMultisig = await get("MultiSigWallet");
await deploy("FixedRateConverter", {
from: deployer,
contract: "FixedRateConverter",
log: true,
skipIfAlreadyDeployed: true,
args: [
deployedMynt.address,
deployedSov.address,
4723550439442834 // sov rate per 1 Mynt; 0.004723550439442834
],
});
};

func.tags = ["FixedRateConverter"];

export default func;
Empty file.
Loading

0 comments on commit a43f74b

Please sign in to comment.