125 lines
4.7 KiB
Solidity
125 lines
4.7 KiB
Solidity
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;
|
|
}
|
|
}
|