-
Notifications
You must be signed in to change notification settings - Fork 246
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
740 additions
and
36 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
// Inspired by AdEx (https://github.com/AdExNetwork/adex-protocol-eth/blob/b9df617829661a7518ee10f4cb6c4108659dd6d5/contracts/libs/SafeERC20.sol) | ||
// and 0x (https://github.com/0xProject/0x-monorepo/blob/737d1dc54d72872e24abce5a1dbe1b66d35fa21a/contracts/protocol/contracts/protocol/AssetProxy/ERC20Proxy.sol#L143) | ||
|
||
pragma solidity ^0.4.24; | ||
|
||
import "../lib/token/ERC20.sol"; | ||
|
||
|
||
library SafeERC20 { | ||
// Before 0.5, solidity has a mismatch between `address.transfer()` and `token.transfer()`: | ||
// https://github.com/ethereum/solidity/issues/3544 | ||
bytes4 private constant TRANSFER_SELECTOR = 0xa9059cbb; | ||
|
||
string private constant ERROR_TOKEN_BALANCE_REVERTED = "SAFE_ERC_20_BALANCE_REVERTED"; | ||
string private constant ERROR_TOKEN_ALLOWANCE_REVERTED = "SAFE_ERC_20_ALLOWANCE_REVERTED"; | ||
|
||
function invokeAndCheckSuccess(address _addr, bytes memory _calldata) | ||
private | ||
returns (bool) | ||
{ | ||
bool ret; | ||
assembly { | ||
let ptr := mload(0x40) // free memory pointer | ||
|
||
let success := call( | ||
gas, // forward all gas | ||
_addr, // address | ||
0, // no value | ||
add(_calldata, 0x20), // calldata start | ||
mload(_calldata), // calldata length | ||
ptr, // write output over free memory | ||
0x20 // uint256 return | ||
) | ||
|
||
if gt(success, 0) { | ||
// Check number of bytes returned from last function call | ||
switch returndatasize | ||
|
||
// No bytes returned: assume success | ||
case 0 { | ||
ret := 1 | ||
} | ||
|
||
// 32 bytes returned: check if non-zero | ||
case 0x20 { | ||
// Only return success if returned data was true | ||
// Already have output in ptr | ||
ret := eq(mload(ptr), 1) | ||
} | ||
|
||
// Not sure what was returned: don't mark as success | ||
default { } | ||
} | ||
} | ||
return ret; | ||
} | ||
|
||
function staticInvoke(address _addr, bytes memory _calldata) | ||
private | ||
view | ||
returns (bool, uint256) | ||
{ | ||
bool success; | ||
uint256 ret; | ||
assembly { | ||
let ptr := mload(0x40) // free memory pointer | ||
|
||
success := staticcall( | ||
gas, // forward all gas | ||
_addr, // address | ||
add(_calldata, 0x20), // calldata start | ||
mload(_calldata), // calldata length | ||
ptr, // write output over free memory | ||
0x20 // uint256 return | ||
) | ||
|
||
if gt(success, 0) { | ||
ret := mload(ptr) | ||
} | ||
} | ||
return (success, ret); | ||
} | ||
|
||
/** | ||
* @dev Same as a standards-compliant ERC20.transfer() that never reverts (returns false). | ||
* Note that this makes an external call to the token. | ||
*/ | ||
function safeTransfer(ERC20 _token, address _to, uint256 _amount) internal returns (bool) { | ||
bytes memory transferCallData = abi.encodeWithSelector( | ||
TRANSFER_SELECTOR, | ||
_to, | ||
_amount | ||
); | ||
return invokeAndCheckSuccess(_token, transferCallData); | ||
} | ||
|
||
/** | ||
* @dev Same as a standards-compliant ERC20.transferFrom() that never reverts (returns false). | ||
* Note that this makes an external call to the token. | ||
*/ | ||
function safeTransferFrom(ERC20 _token, address _from, address _to, uint256 _amount) internal returns (bool) { | ||
bytes memory transferFromCallData = abi.encodeWithSelector( | ||
_token.transferFrom.selector, | ||
_from, | ||
_to, | ||
_amount | ||
); | ||
return invokeAndCheckSuccess(_token, transferFromCallData); | ||
} | ||
|
||
/** | ||
* @dev Same as a standards-compliant ERC20.approve() that never reverts (returns false). | ||
* Note that this makes an external call to the token. | ||
*/ | ||
function safeApprove(ERC20 _token, address _spender, uint256 _amount) internal returns (bool) { | ||
bytes memory approveCallData = abi.encodeWithSelector( | ||
_token.approve.selector, | ||
_spender, | ||
_amount | ||
); | ||
return invokeAndCheckSuccess(_token, approveCallData); | ||
} | ||
|
||
/** | ||
* @dev Static call into ERC20.balanceOf(). | ||
* Reverts if the call fails for some reason (should never fail). | ||
*/ | ||
function staticBalanceOf(ERC20 _token, address _owner) internal view returns (uint256) { | ||
bytes memory balanceOfCallData = abi.encodeWithSelector( | ||
_token.balanceOf.selector, | ||
_owner | ||
); | ||
|
||
(bool success, uint256 tokenBalance) = staticInvoke(_token, balanceOfCallData); | ||
require(success, ERROR_TOKEN_BALANCE_REVERTED); | ||
|
||
return tokenBalance; | ||
} | ||
|
||
/** | ||
* @dev Static call into ERC20.allowance(). | ||
* Reverts if the call fails for some reason (should never fail). | ||
*/ | ||
function staticAllowance(ERC20 _token, address _owner, address _spender) internal view returns (uint256) { | ||
bytes memory allowanceCallData = abi.encodeWithSelector( | ||
_token.allowance.selector, | ||
_owner, | ||
_spender | ||
); | ||
|
||
(bool success, uint256 allowance) = staticInvoke(_token, allowanceCallData); | ||
require(success, ERROR_TOKEN_ALLOWANCE_REVERTED); | ||
|
||
return allowance; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
pragma solidity 0.4.24; | ||
|
||
import "../../common/SafeERC20.sol"; | ||
import "../../lib/token/ERC20.sol"; | ||
|
||
|
||
contract SafeERC20Mock { | ||
using SafeERC20 for ERC20; | ||
event Result(bool result); | ||
|
||
function transfer(ERC20 token, address to, uint256 amount) external returns (bool) { | ||
bool result = token.safeTransfer(to, amount); | ||
emit Result(result); | ||
return result; | ||
} | ||
|
||
function transferFrom(ERC20 token, address from, address to, uint256 amount) external returns (bool) { | ||
bool result = token.safeTransferFrom(from, to, amount); | ||
emit Result(result); | ||
return result; | ||
} | ||
|
||
function approve(ERC20 token, address spender, uint256 amount) external returns (bool) { | ||
bool result = token.safeApprove(spender, amount); | ||
emit Result(result); | ||
return result; | ||
} | ||
|
||
function allowance(ERC20 token, address owner, address spender) external view returns (uint256) { | ||
return token.staticAllowance(owner, spender); | ||
} | ||
|
||
function balanceOf(ERC20 token, address owner) external view returns (uint256) { | ||
return token.staticBalanceOf(owner); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.