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

Kyber integration #33

Merged
merged 3 commits into from Feb 26, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -3,6 +3,7 @@ node_modules
build
dist
coverageEnv
package-lock.json

### Node ###

Expand Down
4 changes: 4 additions & 0 deletions .solcover.js
@@ -0,0 +1,4 @@
module.exports = {
skipFiles: ['kyber/'],
deepSkip: true
};
81 changes: 69 additions & 12 deletions contracts/MyBitBurner.sol
@@ -1,39 +1,99 @@
pragma solidity ^0.4.24;

import "./SafeMath.sol";
import './token/BurnableERC20.sol';

import './token/ERC20.sol';
interface KyberProxy{
function getExpectedRate(address src, address dest, uint srcQty) external view returns (uint expectedRate, uint slippageRate);
function trade(address src, uint srcAmount, address dest, address destAddress, uint maxDestAmount,uint minConversionRate, address walletId) external payable returns(uint);
}
/// @title A contract for burning MYB tokens as usage fee for dapps
/// @author Kyle Dewhurst, MyBit Foundation
/// @notice Allows Dapps to call this contract to burn MYB as a usage fee
/// @dev This contract does not accept tokens. It only burns tokens from users wallets when approved to do so
contract MyBitBurner {
using SafeMath for uint256;

BurnableERC20 public mybToken; // The instance of the MyBitBurner contract
KyberProxy public kyber; // The interface for trading on Kyber
address public owner; // Owner can add or remove authorized contracts
uint256 decimals;

mapping (address => bool) public authorizedBurner; // A mapping showing which addresses are allowed to call the burn function

// @notice constructor: instantiates myb token address and sets owner
// @param (address) _myBitTokenAddress = The MyBit token address
constructor(address _myBitTokenAddress)
constructor(address _myBitTokenAddress, address _kyberAddress)
public {
mybToken = BurnableERC20(_myBitTokenAddress);
kyber = KyberProxy(_kyberAddress);
owner = msg.sender;
uint dec = mybToken.decimals();
decimals = 10**dec;
}

// @notice authorized contracts can burn mybit tokens here if the user has approved this contract to do so
// @param (address) _tokenHolder = the address of the mybit token holder who wishes to burn _amount of tokens
// @param (uint) _amount = the amount of tokens to be burnt (must include decimal places)
function burn(address _tokenHolder, uint _amount)
// @param (address) _burnToken = the address of the token that is being used to pay the fee
function burn(address _tokenHolder, uint _amount, address _burnToken)
payable
external
returns (bool) {
require(authorizedBurner[msg.sender]);
//require(mybToken.allowance(_tokenHolder, address(this)) >= _amount);
require(mybToken.burnFrom(_tokenHolder, _amount));
emit LogMYBBurned(_tokenHolder, msg.sender, _amount);
if(_burnToken == address(mybToken)){
require(mybToken.burnFrom(_tokenHolder, _amount));
emit LogMYBBurned(_tokenHolder, msg.sender, _amount);
} else {
//Calculate the estimate cost of given ERC20 to get convert to correct amount of platform token
(uint expectedRate, uint minRate) = kyber.getExpectedRate(_burnToken, address(mybToken), 0);
uint estimatedCost = expectedRate.mul(_amount).div(decimals);
if(_burnToken == address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)){
//Ether was chosen as the burn token
require(msg.value >= estimatedCost, 'Not enough funds');
convert(_tokenHolder, _burnToken, address(mybToken), msg.value, _amount, minRate, true);
} else {
ERC20 burnToken = ERC20(_burnToken);
//Transfer burn token from the user
require(burnToken.transferFrom(_tokenHolder, address(this), estimatedCost));
// Mitigate ERC20 Approve front-running attack, by initially setting
// allowance to 0
require(burnToken.approve(address(kyber), 0));
// Approve tokens so network can take them during the swap
burnToken.approve(address(kyber), estimatedCost);
convert(_tokenHolder, _burnToken, address(mybToken), estimatedCost, _amount, minRate, false);
}
//Get amount of the platform token held by this contract (in case it differs from the _amount parameter)
uint amount = mybToken.balanceOf(this);
//Burn the platform token
require(mybToken.burn(amount));
emit LogMYBBurned(_tokenHolder, msg.sender, amount);
}
return true;
}

function convert(address _user, address _from, address _to, uint _amount, uint _max, uint _minRate, bool _eth)
private
returns (uint){
uint balanceBefore;
uint change;
emit LogTrade(_from, _amount, _to, address(this), _max, _minRate, address(0));
if(_eth == true){
require(_amount <= address(this).balance, "Not enough funds in contract");
balanceBefore = address(this).balance;
kyber.trade.value(_amount)(_from, _amount, _to, address(this), _max, _minRate, 0);
change = _amount.sub(balanceBefore.sub(address(this).balance));
_user.transfer(change);
} else {
ERC20 erc20 = ERC20(_from);
balanceBefore = erc20.balanceOf(this);
kyber.trade(_from, _amount, _to, address(this), _max, _minRate, 0);
change = _amount.sub(balanceBefore.sub(erc20.balanceOf(this)));
erc20.transfer(_user, change);
}
return change;
}

// @notice owner can authorize a contract to burn MyBit here
// @param the address of the mybit dapp contract
function authorizeBurner(address _burningContract)
Expand All @@ -58,12 +118,8 @@ contract MyBitBurner {
return true;
}

// @notice fallback function. Rejects all ether
function ()
payable
external {
revert();
}
// @notice fallback function. Needs to be open to receive returned Ether from kyber.trade()
function() external payable {}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Modifiers
Expand All @@ -78,4 +134,5 @@ contract MyBitBurner {
event LogMYBBurned(address indexed _tokenHolder, address indexed _burningContract, uint _amount);
event LogBurnerAuthorized(address _owner, address _burningContract);
event LogBurnerRemoved(address _owner, address _burningContract);
event LogTrade(address src, uint amount, address dest, address receiver, uint max, uint minRate, address walletID);
}
6 changes: 3 additions & 3 deletions contracts/TrustERC721.sol
@@ -1,7 +1,7 @@
pragma solidity ^0.4.24;

import './SafeMath.sol';
import './token/IERC721.sol';
import './token/ERC721.sol';

// @title Trust contract
// @author Yossi Pik
Expand All @@ -17,7 +17,7 @@ contract TrustERC721 {

uint public expiration; // Number of seconds until trust expires

IERC721 public token; // The token to be used for the trust
ERC721 public token; // The token to be used for the trust

uint public trustTokenId; // token id intended for beneficiary

Expand All @@ -38,7 +38,7 @@ contract TrustERC721 {
beneficiary = _beneficiary;
revocable = _revocable;
expiration = block.timestamp.add(_expiration);
token = IERC721(_tokenContractAddress);
token = ERC721(_tokenContractAddress);
}

// @notice (payable) trustor can deposit tokens here once
Expand Down
44 changes: 29 additions & 15 deletions contracts/TrustFactory.sol
Expand Up @@ -16,7 +16,7 @@ contract TrustFactory {

MyBitBurner public mybBurner; // The MyBitBurner contract instance

uint public mybFee = uint256(250 * 10**18); // How much MYB to burn in order to create a Trust
uint public mybFee = uint256(250*10**18); // How much MYB to burn in order to create a Trust

// @notice constructor: sets msg.sender as the owner, who has authority to close the factory
constructor(address _mybTokenBurner)
Expand All @@ -29,27 +29,34 @@ contract TrustFactory {
// @param (address) _beneficiary = The address who is to receive ETH from Trust
// @param (bool) _revokeable = Whether or not trustor is able to revoke contract or change _beneficiary
// @param (uint) _expiration = Number of seconds until Trust expires
function deployTrust(address _beneficiary, bool _revokeable, uint _expiration)
function deployTrust(address _beneficiary, bool _revokeable, uint _expiration, address _burnToken)
external
payable {
require(msg.value > 0);
require(!expired);
require(mybBurner.burn(msg.sender, mybFee));
uint amount;
//If burn token is Ether, burn a portion of the trust Ether to pay fees, else burn the token indicated
if(_burnToken == address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)){
amount = ethBurn(msg.value);
} else {
require(mybBurner.burn(msg.sender, mybFee, _burnToken));
amount = msg.value;
}
Trust newTrust = new Trust(msg.sender, _beneficiary, _revokeable, _expiration);
newTrust.depositTrust.value(msg.value)();
emit LogNewTrust(msg.sender, _beneficiary, address(newTrust), msg.value);
newTrust.depositTrust.value(amount)();
emit LogNewTrust(msg.sender, _beneficiary, address(newTrust), amount);
}

// @notice TrustERC20 should be deployed in 2 steps to allow authorization to spend tokens
// @param (address) _beneficiary = The address who is to receive tokens from Trust
// @param (bool) _revokeable = Whether or not trustor is able to revoke contract or change _beneficiary
// @param (uint) _expiration = Number of seconds until Trust expires
// @param (address) _tokenContractAddress = The address of the contract of the token which should be used for the trust
function createTrustERC20(address _beneficiary, bool _revokeable, uint _expiration, address _tokenContractAddress)
function createTrustERC20(address _beneficiary, bool _revokeable, uint _expiration, address _tokenContractAddress, address _burnToken)
external
payable{
require(!expired);
require(mybBurner.burn(msg.sender, mybFee));
require(mybBurner.burn.value(msg.value)(msg.sender, mybFee, _burnToken));
TrustERC20 newTrust = new TrustERC20(msg.sender, _beneficiary, _revokeable, _expiration, _tokenContractAddress);
emit LogNewTrustERC20(msg.sender, _beneficiary, address(newTrust));
}
Expand All @@ -59,11 +66,11 @@ contract TrustFactory {
// @param (bool) _revokeable = Whether or not trustor is able to revoke contract or change _beneficiary
// @param (uint) _expiration = Number of seconds until Trust expires
// @param (address) _tokenContractAddress = The address of the contract of the token which should be used for the trust
function createTrustERC721(address _beneficiary, bool _revokeable, uint _expiration, address _tokenContractAddress)
function createTrustERC721(address _beneficiary, bool _revokeable, uint _expiration, address _tokenContractAddress, address _burnToken)
external
payable{
require(!expired);
require(mybBurner.burn(msg.sender, mybFee));
require(mybBurner.burn.value(msg.value)(msg.sender, mybFee, _burnToken));
TrustERC721 newTrust = new TrustERC721(msg.sender, _beneficiary, _revokeable, _expiration, _tokenContractAddress);
emit LogNewTrustERC721(msg.sender, _beneficiary, address(newTrust));
}
Expand All @@ -86,12 +93,19 @@ contract TrustFactory {
emit LogMYBFeeChange(oldFee, mybFee);
}

// @notice fallback function. Rejects all ether
function ()
external
payable {
revert();
}
// @notice burn fees from Ether payment given, return the change after convert+burn
function ethBurn(uint _amount)
private
returns (uint) {
uint balanceBefore = address(this).balance;
require(mybBurner.burn.value(_amount)(address(this), mybFee, address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)));
uint change = _amount - (balanceBefore - address(this).balance);
require(change <= address(this).balance, "Uh-oh, not enough funds");
return change;
}

// @notice fallback function. Needs to be open to receive returned Ether from ethBurn()
function () external payable {}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down