Add checkAllowance flag to LibTokenSpender.spendERC20Tokens (#39)

This commit is contained in:
mzhu25 2020-11-18 12:23:13 -08:00 committed by GitHub
parent 561b60a24d
commit 89948b360c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 65 additions and 17 deletions

View File

@ -1,4 +1,17 @@
[ [
{
"version": "0.10.0",
"changes": [
{
"note": "Add `checkAllowance` flag to LibTokenSpender.spendERC20Tokens",
"pr": 39
},
{
"note": "Use new `checkAllowance` flag in LiquidityProviderFeature, TransformERC20Feature, and MetaTransactionsFeature",
"pr": 39
}
]
},
{ {
"version": "0.9.0", "version": "0.9.0",
"changes": [ "changes": [

View File

@ -43,7 +43,7 @@ contract LiquidityProviderFeature is
/// @dev Name of this feature. /// @dev Name of this feature.
string public constant override FEATURE_NAME = "LiquidityProviderFeature"; string public constant override FEATURE_NAME = "LiquidityProviderFeature";
/// @dev Version of this feature. /// @dev Version of this feature.
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 0); uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 1);
/// @dev ETH pseudo-token address. /// @dev ETH pseudo-token address.
address constant internal ETH_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; address constant internal ETH_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
@ -60,11 +60,11 @@ contract LiquidityProviderFeature is
address recipient address recipient
); );
constructor(address zeroEx) constructor(LiquidityProviderSandbox sandbox_)
public public
FixinCommon() FixinCommon()
{ {
sandbox = new LiquidityProviderSandbox(zeroEx); sandbox = sandbox_;
} }
/// @dev Initialize and register this feature. /// @dev Initialize and register this feature.
@ -116,7 +116,8 @@ contract LiquidityProviderFeature is
IERC20TokenV06(inputToken), IERC20TokenV06(inputToken),
msg.sender, msg.sender,
provider, provider,
sellAmount sellAmount,
true
); );
} }

View File

@ -78,7 +78,7 @@ contract MetaTransactionsFeature is
/// @dev Name of this feature. /// @dev Name of this feature.
string public constant override FEATURE_NAME = "MetaTransactions"; string public constant override FEATURE_NAME = "MetaTransactions";
/// @dev Version of this feature. /// @dev Version of this feature.
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 0); uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 1, 0);
/// @dev EIP712 typehash of the `MetaTransactionData` struct. /// @dev EIP712 typehash of the `MetaTransactionData` struct.
bytes32 public immutable MTX_EIP712_TYPEHASH = keccak256( bytes32 public immutable MTX_EIP712_TYPEHASH = keccak256(
"MetaTransactionData(" "MetaTransactionData("
@ -283,7 +283,8 @@ contract MetaTransactionsFeature is
mtx.feeToken, mtx.feeToken,
mtx.signer, mtx.signer,
sender, sender,
mtx.feeAmount mtx.feeAmount,
true
); );
} }

View File

@ -815,7 +815,8 @@ contract NativeOrdersFeature is
params.order.takerToken, params.order.takerToken,
params.taker, params.taker,
params.order.feeRecipient, params.order.feeRecipient,
uint256(results.takerTokenFeeFilledAmount) uint256(results.takerTokenFeeFilledAmount),
false
); );
} }
@ -951,7 +952,8 @@ contract NativeOrdersFeature is
settleInfo.takerToken, settleInfo.takerToken,
settleInfo.taker, settleInfo.taker,
settleInfo.maker, settleInfo.maker,
takerTokenFilledAmount takerTokenFilledAmount,
false
); );
// Transfer maker -> taker. // Transfer maker -> taker.
@ -959,7 +961,8 @@ contract NativeOrdersFeature is
settleInfo.makerToken, settleInfo.makerToken,
settleInfo.maker, settleInfo.maker,
settleInfo.taker, settleInfo.taker,
makerTokenFilledAmount makerTokenFilledAmount,
false
); );
} }

View File

@ -58,7 +58,7 @@ contract TransformERC20Feature is
/// @dev Name of this feature. /// @dev Name of this feature.
string public constant override FEATURE_NAME = "TransformERC20"; string public constant override FEATURE_NAME = "TransformERC20";
/// @dev Version of this feature. /// @dev Version of this feature.
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 3, 0); uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 3, 1);
/// @dev Initialize and register this feature. /// @dev Initialize and register this feature.
/// Should be delegatecalled by `Migrate.migrate()`. /// Should be delegatecalled by `Migrate.migrate()`.
@ -317,7 +317,13 @@ contract TransformERC20Feature is
// Transfer input tokens. // Transfer input tokens.
if (!LibERC20Transformer.isTokenETH(inputToken)) { if (!LibERC20Transformer.isTokenETH(inputToken)) {
// Token is not ETH, so pull ERC20 tokens. // Token is not ETH, so pull ERC20 tokens.
LibTokenSpender.spendERC20Tokens(inputToken, from, to, amount); LibTokenSpender.spendERC20Tokens(
inputToken,
from,
to,
amount,
true
);
} else if (msg.value < amount) { } else if (msg.value < amount) {
// Token is ETH, so the caller must attach enough ETH to the call. // Token is ETH, so the caller must attach enough ETH to the call.
LibTransformERC20RichErrors.InsufficientEthAttachedError( LibTransformERC20RichErrors.InsufficientEthAttachedError(

View File

@ -35,11 +35,14 @@ library LibTokenSpender {
/// @param owner The owner of the tokens. /// @param owner The owner of the tokens.
/// @param to The recipient of the tokens. /// @param to The recipient of the tokens.
/// @param amount The amount of `token` to transfer. /// @param amount The amount of `token` to transfer.
/// @param checkAllowance Whether or not to check the owner's allowance
/// prior to attempting the transfer.
function spendERC20Tokens( function spendERC20Tokens(
IERC20TokenV06 token, IERC20TokenV06 token,
address owner, address owner,
address to, address to,
uint256 amount uint256 amount,
bool checkAllowance
) )
internal internal
{ {
@ -48,6 +51,19 @@ library LibTokenSpender {
require(address(token) != address(this), "LibTokenSpender/CANNOT_INVOKE_SELF"); require(address(token) != address(this), "LibTokenSpender/CANNOT_INVOKE_SELF");
if (checkAllowance) {
// If the owner doesn't have a sufficient allowance set on `address(this)`,
// try the old AllowanceTarget.
if (token.allowance(owner, address(this)) < amount) {
return ITokenSpenderFeature(address(this))._spendERC20Tokens(
token,
owner,
to,
amount
);
}
}
assembly { assembly {
let ptr := mload(0x40) // free memory pointer let ptr := mload(0x40) // free memory pointer

View File

@ -34,7 +34,13 @@ contract TestLibTokenSpender {
) )
external external
{ {
LibTokenSpender.spendERC20Tokens(token, owner, to, amount); LibTokenSpender.spendERC20Tokens(
token,
owner,
to,
amount,
false
);
} }
event FallbackCalled( event FallbackCalled(

View File

@ -51,17 +51,19 @@ blockchainTests('LiquidityProvider feature', env => {
.awaitTransactionSuccessAsync({ from: taker }); .awaitTransactionSuccessAsync({ from: taker });
feature = new LiquidityProviderFeatureContract(zeroEx.address, env.provider, env.txDefaults, abis); feature = new LiquidityProviderFeatureContract(zeroEx.address, env.provider, env.txDefaults, abis);
const featureImpl = await LiquidityProviderFeatureContract.deployFrom0xArtifactAsync( sandbox = await LiquidityProviderSandboxContract.deployFrom0xArtifactAsync(
artifacts.LiquidityProviderFeature, artifacts.LiquidityProviderSandbox,
env.provider, env.provider,
env.txDefaults, env.txDefaults,
artifacts, artifacts,
zeroEx.address, zeroEx.address,
); );
sandbox = new LiquidityProviderSandboxContract( const featureImpl = await LiquidityProviderFeatureContract.deployFrom0xArtifactAsync(
await featureImpl.sandbox().callAsync(), artifacts.LiquidityProviderFeature,
env.provider, env.provider,
env.txDefaults, env.txDefaults,
artifacts,
sandbox.address,
); );
await new IOwnableFeatureContract(zeroEx.address, env.provider, env.txDefaults, abis) await new IOwnableFeatureContract(zeroEx.address, env.provider, env.txDefaults, abis)
.migrate(featureImpl.address, featureImpl.migrate().getABIEncodedTransactionData(), owner) .migrate(featureImpl.address, featureImpl.migrate().getABIEncodedTransactionData(), owner)