Skip to content

Commit

Permalink
Integrate with Kyber and 1inch to mint mAsset with ETH (#73) (#84)
Browse files Browse the repository at this point in the history
* 1inch (OneSplit) DEX integration
  • Loading branch information
superduck35 authored Jun 18, 2020
1 parent 82659bc commit f9f87c7
Show file tree
Hide file tree
Showing 8 changed files with 996 additions and 0 deletions.
77 changes: 77 additions & 0 deletions contracts/integrations/AbstractBuyAndMint.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
pragma solidity 0.5.16;

// Internal
import { MassetHelpers } from "../masset/shared/MassetHelpers.sol";

// Library
import { Ownable } from "@openzeppelin/contracts/ownership/Ownable.sol";

/**
* @title AbstractBuyAndMint
* @author Stability Labs Pty. Ltd.
* @notice Abstract contract to allow buy bAsset tokens with ETH and mint mAssets tokens
* from mStable.
*/
contract AbstractBuyAndMint is Ownable {

using MassetHelpers for address;

event MassetAdded(address indexed mAsset);

// mAsset address => exists
mapping(address => bool) public mAssets;

/**
* @dev Abstarct constructor
* @param _mAssets Array of valid mAsset addresses allowed to mint.
*/
constructor(address[] memory _mAssets) internal {
require(_mAssets.length > 0, "No mAssets provided");
for(uint256 i = 0; i < _mAssets.length; i++) {
_addMasset(_mAssets[i]);
}
}

/**
* @dev Anyone can call and perform infinite approval for bAssets
* @param _bAssets An array containing bAssets addresses
*/
function infiniteApprove(address _mAsset, address[] calldata _bAssets) external {
for(uint256 i = 0; i < _bAssets.length; i++) {
_bAssets[i].safeInfiniteApprove(_mAsset);
}
}

/**
* @dev The Owner of the contract allowed to add a new supported mAsset.
* @param _mAsset Address of the mAsset
*/
function addMasset(address _mAsset) external onlyOwner {
_addMasset(_mAsset);
}

/**
* @dev Add a new mAsset to the supported mAssets list
* @param _mAsset Address of the mAsset
*/
function _addMasset(address _mAsset) internal {
require(_mAsset != address(0), "mAsset address is zero");
require(!_massetExists(_mAsset), "mAsset already exists");
mAssets[_mAsset] = true;
emit MassetAdded(_mAsset);
}

/**
* @dev Validate that the given mAsset supported by this contract.
* @notice Only validate mAsset address. As bAsset gets validated during minting process.
* @param _mAsset mAsset address to validate
*/
function _massetExists(address _mAsset) internal view returns (bool) {
return mAssets[_mAsset];
}

/**
* @dev Abstract function to get the external DEX contract address
*/
function _externalDexAddress() internal view returns(address);
}
23 changes: 23 additions & 0 deletions contracts/integrations/IMintWith1Inch.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
pragma solidity 0.5.16;

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

/**
* @title IMintWith1Inch
* @author Stability Labs Pty. Ltd.
* @notice Interface to Buy bAssets and Mint the mAssets from the OneSplit (1inch exchange)
*/
interface IMintWith1Inch {
/**
* @dev Buy the maximum bAsset tokens from DEX and mint mAsset tokens from mStable.
* ETH sent to the function used to buy bAsset tokens from DEX.
* @param _srcBasset Source bAsset token address
* @param _destMasset mAsset token address to mint
* @param _minBassetUnits Minimum amount of bAssets to purchase
* @param _distribution Distribution for different DEXes to buy bAssets from
* @return mAssetQtyMinted Returns the quantity of mAsset minted from mStable
*/
function buyAndMint(IERC20 _srcBasset, address _destMasset, uint256 _minBassetUnits, uint256[] calldata _distribution)
external payable returns (uint256 mAssetQtyMinted);

}
108 changes: 108 additions & 0 deletions contracts/integrations/MintWith1Inch.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
pragma solidity 0.5.16;

// External
import { AbstractBuyAndMint } from "./AbstractBuyAndMint.sol";
import { IMintWith1Inch } from "./IMintWith1Inch.sol";
import { IMasset } from "../interfaces/IMasset.sol";

// Libs
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";

/**
* @dev OneSplit Exchange interface
*/
contract IOneSplit {
function swap(
IERC20 fromToken,
IERC20 toToken,
uint256 amount,
uint256 minReturn,
uint256[] memory distribution,
uint256 flags
) public payable;
}

/**
* @title MintWith1Inch
* @author Stability Labs Pty. Ltd.
* @notice Contract integrates with 1inch (a.k.a OneSplit) contract and allows anyone to buy
* bAsset tokens using ETH from the 1inch and mint mAssets.
*/
contract MintWith1Inch is AbstractBuyAndMint, IMintWith1Inch {

using SafeMath for uint256;

// 1inch Exchange 1Split contract address
IOneSplit public oneSplit;

IERC20 private constant ETH_ADDRESS = IERC20(0x0000000000000000000000000000000000000000);

/**
* @param _oneSplit OneSplit contract address
* @param _mAssets Array of mAssets addresses
*/
constructor(address _oneSplit, address[] memory _mAssets)
public
AbstractBuyAndMint(_mAssets)
{
require(_oneSplit != address(0), "1inch address is zero");

oneSplit = IOneSplit(_oneSplit);
}

// @override
function _externalDexAddress() internal view returns(address) {
return address(oneSplit);
}

// NOTICE: Make the following function call off-chain to get the `distribution` and
// pass to this function. This is to reduce gas consumption.

// ============================================================================
// Offchain: To calculate the distribution to mint max mAssets with ETH amount
// ============================================================================
/*
// Parts = 20 (Suggested by 1inch to provide best rates)
uint256 parts = 20;
// Not disable any exchange
uint256 disableFlags = 0;
(,uint256[] memory distribution) =
oneSplit.getExpectedReturn(
ETH_ADDRESS, //fromToken
IERC20(_srcBasset), //toToken
msg.value, //fromAmount
parts, //parts
disableFlags //disableFlags
);
*/

// @override
function buyAndMint(
IERC20 _srcBasset,
address _destMasset,
uint256 _minBassetUnits,
uint256[] calldata _distribution
)
external
payable
returns (uint256 mAssetQtyMinted)
{
require(msg.value > 0, "ETH not sent");
require(_massetExists(_destMasset), "Not a valid mAsset");

// 1. Buy bAsset of worth `msg.value` ETH from OneSplit
oneSplit.swap.value(msg.value)(
ETH_ADDRESS,
_srcBasset,
msg.value,
_minBassetUnits,
_distribution,
0
);

uint256 balance = _srcBasset.balanceOf(address(this));
// 2. Mint mAsset with all bAsset
mAssetQtyMinted = IMasset(_destMasset).mintTo(address(_srcBasset), balance, _msgSender());
}
}
43 changes: 43 additions & 0 deletions contracts/z_mocks/integrations/IBuyAndMint.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
pragma solidity 0.5.16;

/**
* @title IBuyAndMint
* @author Stability Labs Pty. Ltd.
* @notice Interface to Buy bAssets and Mint the mAssets from the DEXes
*/
interface IBuyAndMint {
/**
* @dev Buy the maximum bAsset tokens from DEX and mint mAsset tokens from mStable.
* ETH sent to the function used to buy bAsset tokens from DEX.
* @param _srcBasset Source bAsset token address
* @param _destMasset mAsset token address to mint
* @return mAssetQtyMinted Returns the quantity of mAsset minted from mStable
*/
function buyAndMintMaxMasset(address _srcBasset, address _destMasset)
external payable returns (uint256 mAssetQtyMinted);

/**
* @dev Buy the required bAsset tokens from the DEX and Mint the specific amount of
* mAssets from mStable. ETH sent to the function used to buy bAsset tokens from DEX.
* @param _srcBasset Source bAsset token address
* @param _destMasset mAsset token address to mint
* @param _amountOfBassets Expected amount of bAssets to mint from DEX and mint 1:1 from mStable
* @return mAssetQtyMinted Returns the quantity of mAsset minted from mStable
*/
function buyAndMintGivenMasset(address _srcBasset, address _destMasset, uint256 _amountOfBassets)
external payable returns (uint256 mAssetQtyMinted);

/**
* @dev Buy the required bAssets tokens from the DEX and Mint mAssets from mStable.
* ETH sent to the function used to buy bAsset tokens from DEX.
* @param _srcBassets Array of source bAssets token address
* @param _ethAmount Array of ETH amount to buy corresponding bAsset from DEX
* @param _destMAsset mAsset token address to mint
* @return mAssetQtyMinted Returns the quantity of mAsset minted from mStable
*/
function buyAndMintMulti(
address[] calldata _srcBassets,
uint256[] calldata _ethAmount,
address _destMAsset
) external payable returns (uint256 mAssetQtyMinted);
}
Loading

0 comments on commit f9f87c7

Please sign in to comment.