92 lines
3.2 KiB
Solidity
92 lines
3.2 KiB
Solidity
|
// Brought from https://github.com/aragon/aragonOS/blob/v4.3.0/contracts/common/SafeERC20.sol
|
||
|
// Adapted to use pragma ^0.5.8 and satisfy our linter rules
|
||
|
|
||
|
pragma solidity ^0.5.8;
|
||
|
|
||
|
import "./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;
|
||
|
|
||
|
/**
|
||
|
* @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(address(_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(address(_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(address(_token), approveCallData);
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
}
|