Make SwapProxy ownable and SafeToken

clean up SwapProxy
This commit is contained in:
Barry Gitarts 2019-10-11 15:47:52 -04:00 committed by Barry G
parent 2a8af2f1a9
commit e0a866977c
6 changed files with 443 additions and 20 deletions

View File

@ -1,6 +1,8 @@
pragma solidity ^0.4.18;
import "./LiquidPledging.sol";
import "./common/SafeToken.sol";
import "./common/Ownable.sol";
// On mainnet extract the values from here: https://developer.kyber.network/docs/Environments-Mainnet/
@ -13,23 +15,12 @@ contract KyberNetworkProxy {
function swapEtherToToken(address token, uint minConversionRate) public payable returns(uint);
}
interface ERC20Token {
function transfer(address _to, uint256 _value) external returns (bool success);
function approve(address _spender, uint256 _value) external returns (bool success);
function transferFrom(address _from, address _to, uint256 _value) external returns (bool success);
function balanceOf(address _owner) external view returns (uint256 balance);
function allowance(address _owner, address _spender) external view returns (uint256 remaining);
function totalSupply() external view returns (uint256 supply);
event Transfer(address indexed _from, address indexed _to, uint256 _value);
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
}
contract SwapProxy {
contract SwapProxy is Ownable, SafeToken {
address public ETH;
KyberNetworkProxy public kyberProxy;
LiquidPledging public liquidPledging;
address public vault;
uint public maxSlippage;
KyberNetworkProxy public kyberProxy;
LiquidPledging public liquidPledging;
/**
* @param _liquidPledging LiquidPledging contract address
@ -88,7 +79,7 @@ contract SwapProxy {
uint amount = kyberProxy.trade.value(msg.value)(ETH, msg.value, token, address(this), maxDestinationAmount, slippageRate, vault);
require(amount > 0);
ERC20Token(token).approve(address(liquidPledging), amount);
require(EIP20Interface(token).approve(address(liquidPledging), amount));
liquidPledging.addGiverAndDonate(idReceiver, token, amount);
}
@ -100,7 +91,8 @@ contract SwapProxy {
* @param receiverToken token being converted to
*/
function fundWithToken(uint64 idReceiver, address token, uint amount, address receiverToken) public {
require(ERC20Token(token).transferFrom(msg.sender, address(this), amount));
Error err = doTransferIn(token, msg.sender, amount);
require(err == Error.NO_ERROR);
uint expectedRate;
uint slippageRate;
@ -108,15 +100,20 @@ contract SwapProxy {
require(expectedRate > 0);
uint slippagePercent = (slippageRate * 100) / expectedRate;
require(slippagePercent <= maxSlippage);
require(ERC20Token(token).approve(address(kyberProxy), 0));
require(ERC20Token(token).approve(address(kyberProxy), amount));
require(EIP20Interface(token).approve(address(kyberProxy), 0));
require(EIP20Interface(token).approve(address(kyberProxy), amount));
uint maxDestinationAmount = (slippageRate / (10**18)) * amount;
uint receiverAmount = kyberProxy.trade(token, amount, receiverToken, address(this), maxDestinationAmount, slippageRate, vault);
require(receiverAmount > 0);
ERC20Token(token).approve(address(liquidPledging), receiverAmount);
require(EIP20Interface(token).approve(address(liquidPledging), receiverAmount));
liquidPledging.addGiverAndDonate(idReceiver, receiverToken, receiverAmount);
}
function transferOut(address asset, address to, uint amount) public onlyOwner {
Error err = doTransferOut(asset, to, amount);
require(err == Error.NO_ERROR);
}
function() payable external {}
}

View File

@ -0,0 +1,49 @@
// Abstract contract for the full ERC 20 Token standard
// https://github.com/ethereum/EIPs/issues/20
pragma solidity ^0.4.18;
contract EIP20Interface {
/* This is a slight change to the ERC20 base standard.
function totalSupply() constant returns (uint256 supply);
is replaced with:
uint256 public totalSupply;
This automatically creates a getter function for the totalSupply.
This is moved to the base contract since public getter functions are not
currently recognised as an implementation of the matching abstract
function by the compiler.
*/
/// total amount of tokens
uint256 public totalSupply;
/// @param _owner The address from which the balance will be retrieved
/// @return The balance
function balanceOf(address _owner) public view returns (uint256 balance);
/// @notice send `_value` token to `_to` from `msg.sender`
/// @param _to The address of the recipient
/// @param _value The amount of token to be transferred
/// @return Whether the transfer was successful or not
function transfer(address _to, uint256 _value) public returns (bool success);
/// @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from`
/// @param _from The address of the sender
/// @param _to The address of the recipient
/// @param _value The amount of token to be transferred
/// @return Whether the transfer was successful or not
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success);
/// @notice `msg.sender` approves `_spender` to spend `_value` tokens
/// @param _spender The address of the account able to transfer the tokens
/// @param _value The amount of tokens to be approved for transfer
/// @return Whether the approval was successful or not
function approve(address _spender, uint256 _value) public returns (bool success);
/// @param _owner The address of the account owning tokens
/// @param _spender The address of the account able to transfer the tokens
/// @return Amount of remaining tokens allowed to spent
function allowance(address _owner, address _spender) public view returns (uint256 remaining);
// solhint-disable-next-line no-simple-event-func-name
event Transfer(address indexed _from, address indexed _to, uint256 _value);
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
}

View File

@ -0,0 +1,66 @@
// Abstract contract for the full ERC 20 Token standard
// https://github.com/ethereum/EIPs/issues/20
pragma solidity ^0.4.18;
/**
* @title EIP20NonStandardInterface
* @dev Version of ERC20 with no return values for `transfer` and `transferFrom`
* See https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca
*/
contract EIP20NonStandardInterface {
/* This is a slight change to the ERC20 base standard.
function totalSupply() constant returns (uint256 supply);
is replaced with:
uint256 public totalSupply;
This automatically creates a getter function for the totalSupply.
This is moved to the base contract since public getter functions are not
currently recognised as an implementation of the matching abstract
function by the compiler.
*/
/// total amount of tokens
uint256 public totalSupply;
/// @param _owner The address from which the balance will be retrieved
/// @return The balance
function balanceOf(address _owner) public view returns (uint256 balance);
///
/// !!!!!!!!!!!!!!
/// !!! NOTICE !!! `transfer` does not return a value, in violation of the ERC-20 specification
/// !!!!!!!!!!!!!!
///
/// @notice send `_value` token to `_to` from `msg.sender`
/// @param _to The address of the recipient
/// @param _value The amount of token to be transferred
/// @return Whether the transfer was successful or not
function transfer(address _to, uint256 _value) public;
///
/// !!!!!!!!!!!!!!
/// !!! NOTICE !!! `transferFrom` does not return a value, in violation of the ERC-20 specification
/// !!!!!!!!!!!!!!
///
/// @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from`
/// @param _from The address of the sender
/// @param _to The address of the recipient
/// @param _value The amount of token to be transferred
/// @return Whether the transfer was successful or not
function transferFrom(address _from, address _to, uint256 _value) public;
/// @notice `msg.sender` approves `_spender` to spend `_value` tokens
/// @param _spender The address of the account able to transfer the tokens
/// @param _value The amount of tokens to be approved for transfer
/// @return Whether the approval was successful or not
function approve(address _spender, uint256 _value) public returns (bool success);
/// @param _owner The address of the account owning tokens
/// @param _spender The address of the account able to transfer the tokens
/// @return Amount of remaining tokens allowed to spent
function allowance(address _owner, address _spender) public view returns (uint256 remaining);
// solhint-disable-next-line no-simple-event-func-name
event Transfer(address indexed _from, address indexed _to, uint256 _value);
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
}

View File

@ -0,0 +1,161 @@
pragma solidity ^0.4.18;
contract ErrorReporter {
/**
* @dev `error` corresponds to enum Error; `info` corresponds to enum FailureInfo, and `detail` is an arbitrary
* contract-specific code that enables us to report opaque error codes from upgradeable contracts.
**/
event Failure(uint error, uint info, uint detail);
enum Error {
NO_ERROR,
OPAQUE_ERROR, // To be used when reporting errors from upgradeable contracts; the opaque code should be given as `detail` in the `Failure` event
UNAUTHORIZED,
INTEGER_OVERFLOW,
INTEGER_UNDERFLOW,
DIVISION_BY_ZERO,
BAD_INPUT,
TOKEN_INSUFFICIENT_ALLOWANCE,
TOKEN_INSUFFICIENT_BALANCE,
TOKEN_TRANSFER_FAILED,
MARKET_NOT_SUPPORTED,
SUPPLY_RATE_CALCULATION_FAILED,
BORROW_RATE_CALCULATION_FAILED,
TOKEN_INSUFFICIENT_CASH,
TOKEN_TRANSFER_OUT_FAILED,
INSUFFICIENT_LIQUIDITY,
INSUFFICIENT_BALANCE,
INVALID_COLLATERAL_RATIO,
MISSING_ASSET_PRICE,
EQUITY_INSUFFICIENT_BALANCE,
INVALID_CLOSE_AMOUNT_REQUESTED,
ASSET_NOT_PRICED,
INVALID_LIQUIDATION_DISCOUNT,
INVALID_COMBINED_RISK_PARAMETERS,
ZERO_ORACLE_ADDRESS,
CONTRACT_PAUSED
}
/*
* Note: FailureInfo (but not Error) is kept in alphabetical order
* This is because FailureInfo grows significantly faster, and
* the order of Error has some meaning, while the order of FailureInfo
* is entirely arbitrary.
*/
enum FailureInfo {
ACCEPT_ADMIN_PENDING_ADMIN_CHECK,
BORROW_ACCOUNT_LIQUIDITY_CALCULATION_FAILED,
BORROW_ACCOUNT_SHORTFALL_PRESENT,
BORROW_ACCUMULATED_BALANCE_CALCULATION_FAILED,
BORROW_AMOUNT_LIQUIDITY_SHORTFALL,
BORROW_AMOUNT_VALUE_CALCULATION_FAILED,
BORROW_CONTRACT_PAUSED,
BORROW_MARKET_NOT_SUPPORTED,
BORROW_NEW_BORROW_INDEX_CALCULATION_FAILED,
BORROW_NEW_BORROW_RATE_CALCULATION_FAILED,
BORROW_NEW_SUPPLY_INDEX_CALCULATION_FAILED,
BORROW_NEW_SUPPLY_RATE_CALCULATION_FAILED,
BORROW_NEW_TOTAL_BALANCE_CALCULATION_FAILED,
BORROW_NEW_TOTAL_BORROW_CALCULATION_FAILED,
BORROW_NEW_TOTAL_CASH_CALCULATION_FAILED,
BORROW_ORIGINATION_FEE_CALCULATION_FAILED,
BORROW_TRANSFER_OUT_FAILED,
EQUITY_WITHDRAWAL_AMOUNT_VALIDATION,
EQUITY_WITHDRAWAL_CALCULATE_EQUITY,
EQUITY_WITHDRAWAL_MODEL_OWNER_CHECK,
EQUITY_WITHDRAWAL_TRANSFER_OUT_FAILED,
LIQUIDATE_ACCUMULATED_BORROW_BALANCE_CALCULATION_FAILED,
LIQUIDATE_ACCUMULATED_SUPPLY_BALANCE_CALCULATION_FAILED_BORROWER_COLLATERAL_ASSET,
LIQUIDATE_ACCUMULATED_SUPPLY_BALANCE_CALCULATION_FAILED_LIQUIDATOR_COLLATERAL_ASSET,
LIQUIDATE_AMOUNT_SEIZE_CALCULATION_FAILED,
LIQUIDATE_BORROW_DENOMINATED_COLLATERAL_CALCULATION_FAILED,
LIQUIDATE_CLOSE_AMOUNT_TOO_HIGH,
LIQUIDATE_CONTRACT_PAUSED,
LIQUIDATE_DISCOUNTED_REPAY_TO_EVEN_AMOUNT_CALCULATION_FAILED,
LIQUIDATE_NEW_BORROW_INDEX_CALCULATION_FAILED_BORROWED_ASSET,
LIQUIDATE_NEW_BORROW_INDEX_CALCULATION_FAILED_COLLATERAL_ASSET,
LIQUIDATE_NEW_BORROW_RATE_CALCULATION_FAILED_BORROWED_ASSET,
LIQUIDATE_NEW_SUPPLY_INDEX_CALCULATION_FAILED_BORROWED_ASSET,
LIQUIDATE_NEW_SUPPLY_INDEX_CALCULATION_FAILED_COLLATERAL_ASSET,
LIQUIDATE_NEW_SUPPLY_RATE_CALCULATION_FAILED_BORROWED_ASSET,
LIQUIDATE_NEW_TOTAL_BORROW_CALCULATION_FAILED_BORROWED_ASSET,
LIQUIDATE_NEW_TOTAL_CASH_CALCULATION_FAILED_BORROWED_ASSET,
LIQUIDATE_NEW_TOTAL_SUPPLY_BALANCE_CALCULATION_FAILED_BORROWER_COLLATERAL_ASSET,
LIQUIDATE_NEW_TOTAL_SUPPLY_BALANCE_CALCULATION_FAILED_LIQUIDATOR_COLLATERAL_ASSET,
LIQUIDATE_FETCH_ASSET_PRICE_FAILED,
LIQUIDATE_TRANSFER_IN_FAILED,
LIQUIDATE_TRANSFER_IN_NOT_POSSIBLE,
REPAY_BORROW_ACCUMULATED_BALANCE_CALCULATION_FAILED,
REPAY_BORROW_CONTRACT_PAUSED,
REPAY_BORROW_NEW_BORROW_INDEX_CALCULATION_FAILED,
REPAY_BORROW_NEW_BORROW_RATE_CALCULATION_FAILED,
REPAY_BORROW_NEW_SUPPLY_INDEX_CALCULATION_FAILED,
REPAY_BORROW_NEW_SUPPLY_RATE_CALCULATION_FAILED,
REPAY_BORROW_NEW_TOTAL_BALANCE_CALCULATION_FAILED,
REPAY_BORROW_NEW_TOTAL_BORROW_CALCULATION_FAILED,
REPAY_BORROW_NEW_TOTAL_CASH_CALCULATION_FAILED,
REPAY_BORROW_TRANSFER_IN_FAILED,
REPAY_BORROW_TRANSFER_IN_NOT_POSSIBLE,
SET_ASSET_PRICE_CHECK_ORACLE,
SET_MARKET_INTEREST_RATE_MODEL_OWNER_CHECK,
SET_ORACLE_OWNER_CHECK,
SET_ORIGINATION_FEE_OWNER_CHECK,
SET_PAUSED_OWNER_CHECK,
SET_PENDING_ADMIN_OWNER_CHECK,
SET_RISK_PARAMETERS_OWNER_CHECK,
SET_RISK_PARAMETERS_VALIDATION,
SUPPLY_ACCUMULATED_BALANCE_CALCULATION_FAILED,
SUPPLY_CONTRACT_PAUSED,
SUPPLY_MARKET_NOT_SUPPORTED,
SUPPLY_NEW_BORROW_INDEX_CALCULATION_FAILED,
SUPPLY_NEW_BORROW_RATE_CALCULATION_FAILED,
SUPPLY_NEW_SUPPLY_INDEX_CALCULATION_FAILED,
SUPPLY_NEW_SUPPLY_RATE_CALCULATION_FAILED,
SUPPLY_NEW_TOTAL_BALANCE_CALCULATION_FAILED,
SUPPLY_NEW_TOTAL_CASH_CALCULATION_FAILED,
SUPPLY_NEW_TOTAL_SUPPLY_CALCULATION_FAILED,
SUPPLY_TRANSFER_IN_FAILED,
SUPPLY_TRANSFER_IN_NOT_POSSIBLE,
SUPPORT_MARKET_FETCH_PRICE_FAILED,
SUPPORT_MARKET_OWNER_CHECK,
SUPPORT_MARKET_PRICE_CHECK,
SUSPEND_MARKET_OWNER_CHECK,
WITHDRAW_ACCOUNT_LIQUIDITY_CALCULATION_FAILED,
WITHDRAW_ACCOUNT_SHORTFALL_PRESENT,
WITHDRAW_ACCUMULATED_BALANCE_CALCULATION_FAILED,
WITHDRAW_AMOUNT_LIQUIDITY_SHORTFALL,
WITHDRAW_AMOUNT_VALUE_CALCULATION_FAILED,
WITHDRAW_CAPACITY_CALCULATION_FAILED,
WITHDRAW_CONTRACT_PAUSED,
WITHDRAW_NEW_BORROW_INDEX_CALCULATION_FAILED,
WITHDRAW_NEW_BORROW_RATE_CALCULATION_FAILED,
WITHDRAW_NEW_SUPPLY_INDEX_CALCULATION_FAILED,
WITHDRAW_NEW_SUPPLY_RATE_CALCULATION_FAILED,
WITHDRAW_NEW_TOTAL_BALANCE_CALCULATION_FAILED,
WITHDRAW_NEW_TOTAL_SUPPLY_CALCULATION_FAILED,
WITHDRAW_TRANSFER_OUT_FAILED,
WITHDRAW_TRANSFER_OUT_NOT_POSSIBLE
}
/**
* @dev use this when reporting a known error
*/
function fail(Error err, FailureInfo info) internal returns (uint) {
Failure(uint(err), uint(info), 0);
return uint(err);
}
/**
* @dev use this when reporting an opaque error from an upgradeable collaborator contract
*/
function failOpaque(FailureInfo info, uint opaqueError) internal returns (uint) {
Failure(uint(Error.OPAQUE_ERROR), uint(info), opaqueError);
return uint(Error.OPAQUE_ERROR);
}
}

View File

@ -0,0 +1,26 @@
pragma solidity ^0.4.18;
/*
* Ownable
*
* Base contract with an owner.
* Provides onlyOwner modifier, which prevents function from running if it is called by anyone other than the owner.
*/
contract Ownable {
address public owner;
function Ownable() {
owner = msg.sender;
}
modifier onlyOwner() {
if (msg.sender == owner)
_;
}
function transferOwnership(address newOwner) onlyOwner {
if (newOwner != address(0)) owner = newOwner;
}
}

View File

@ -0,0 +1,124 @@
pragma solidity ^0.4.18;
import "./EIP20Interface.sol";
import "./EIP20NonStandardInterface.sol";
import "./ErrorReporter.sol";
/**
* @title Safe Token
* @author Compound
* @notice This is a work in progress.
*/
contract SafeToken is ErrorReporter {
/**
* @dev Checks whether or not there is sufficient allowance for this contract to move amount from `from` and
* whether or not `from` has a balance of at least `amount`. Does NOT do a transfer.
*/
function checkTransferIn(address asset, address from, uint amount) internal view returns (Error) {
EIP20Interface token = EIP20Interface(asset);
if (token.allowance(from, address(this)) < amount) {
return Error.TOKEN_INSUFFICIENT_ALLOWANCE;
}
if (token.balanceOf(from) < amount) {
return Error.TOKEN_INSUFFICIENT_BALANCE;
}
return Error.NO_ERROR;
}
/**
* @dev Similar to EIP20 transfer, except it handles a False result from `transferFrom` and returns an explanatory
* error code rather than reverting. If caller has not called `checkTransferIn`, this may revert due to
* insufficient balance or insufficient allowance. If caller has called `checkTransferIn` prior to this call,
* and it returned Error.NO_ERROR, this should not revert in normal conditions.
*
* Note: This wrapper safely handles non-standard ERC-20 tokens that do not return a value.
* See here: https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca
*/
function doTransferIn(address asset, address from, uint amount) internal returns (Error) {
EIP20NonStandardInterface token = EIP20NonStandardInterface(asset);
bool result;
token.transferFrom(from, address(this), amount);
assembly {
switch returndatasize()
case 0 { // This is a non-standard ERC-20
result := not(0) // set result to true
}
case 32 { // This is a complaint ERC-20
returndatacopy(0, 0, 32)
result := mload(0) // Set `result = returndata` of external call
}
default { // This is an excessively non-compliant ERC-20, revert.
revert(0, 0)
}
}
if (!result) {
return Error.TOKEN_TRANSFER_FAILED;
}
return Error.NO_ERROR;
}
/**
* @dev Checks balance of this contract in asset
*/
function getCash(address asset) internal view returns (uint) {
EIP20Interface token = EIP20Interface(asset);
return token.balanceOf(address(this));
}
/**
* @dev Checks balance of `from` in `asset`
*/
function getBalanceOf(address asset, address from) internal view returns (uint) {
EIP20Interface token = EIP20Interface(asset);
return token.balanceOf(from);
}
/**
* @dev Similar to EIP20 transfer, except it handles a False result from `transfer` and returns an explanatory
* error code rather than reverting. If caller has not called checked protocol's balance, this may revert due to
* insufficient cash held in this contract. If caller has checked protocol's balance prior to this call, and verified
* it is >= amount, this should not revert in normal conditions.
*
* Note: This wrapper safely handles non-standard ERC-20 tokens that do not return a value.
* See here: https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca
*/
function doTransferOut(address asset, address to, uint amount) internal returns (Error) {
EIP20NonStandardInterface token = EIP20NonStandardInterface(asset);
bool result;
token.transfer(to, amount);
assembly {
switch returndatasize()
case 0 { // This is a non-standard ERC-20
result := not(0) // set result to true
}
case 32 { // This is a complaint ERC-20
returndatacopy(0, 0, 32)
result := mload(0) // Set `result = returndata` of external call
}
default { // This is an excessively non-compliant ERC-20, revert.
revert(0, 0)
}
}
if (!result) {
return Error.TOKEN_TRANSFER_OUT_FAILED;
}
return Error.NO_ERROR;
}
}