bsc<->development rebase (#189)
* FQT: Pack Protocol/source name into source ID (#162) * `@0x/contracts-zero-ex`: Encode protocol ID and source name in bridge source ID `@0x/asset-swapper`: Use new bridge source ID encoding. * fix linter issues * contracts cleanup (#164) * `@0x/contracts-zero-ex`: Add PancakeSwapFeature * `@0x/contracts-zero-ex`: Remove tokenspender/allowance target/greedy tokens stuff.' `@0x/contract-addresses`: Add BSC addresses. Remove exchangeProxyAllowanceTarget. `@0x/migrations`: Remove exchangeProxyAllowanceTarget. * Update contracts/zero-ex/contracts/src/features/IPancakeSwapFeature.sol Co-authored-by: mzhu25 <mchl.zhu.96@gmail.com> * `@0x/contracts-zero-ex`: Add sushiswap support to PancakeSwap * `@0x/contract-artifacts`: Regenerate artifacts `@0x/contract-wrappers`: Regenerate wrappers * `@0x/contract-addresses`: Add BSC addresses Co-authored-by: mzhu25 <mchl.zhu.96@gmail.com> Co-authored-by: mzhu25 <mchl.zhu.96@gmail.com> * feat: Better chain support (#163) * feat: Better chain support * feat: better chain support refactor deployment constants (#166) * proliferate the chainId * Refactor sampler to remove DeploymentConstants dependency and fixed addresses * Rework WETH out, replacing with address(0) * wat * hack DeploymentConstants for now * proliferate the chainId * Refactor sampler to remove DeploymentConstants dependency and fixed addresses * remove duped network addresses * Rework the bridge source encoder * Use the constants NATIVE_FEE_TOKEN in EP consumer * `@0x/contract-addresses`: Fix WBNB address (#170) Co-authored-by: Lawrence Forman <lawrence@0xproject.com> * multichain enable cakez vip (#171) * feat: Better chain support * feat: better chain support refactor deployment constants (#166) * proliferate the chainId * Refactor sampler to remove DeploymentConstants dependency and fixed addresses * Rework WETH out, replacing with address(0) * wat * hack DeploymentConstants for now * proliferate the chainId * Refactor sampler to remove DeploymentConstants dependency and fixed addresses * remove duped network addresses * `asset-swapper`: enable pancake VIP route generation Co-authored-by: Jacob Evans <jacob@dekz.net> Co-authored-by: Lawrence Forman <me@merklejerk.com> * `@0x/contracts-zero-ex`: Fix `PancakeSwapFeature` sushi values (#172) * `@0x/contracts-zero-ex`: Fix `PancakeSwapFeature` sushi values * `@0x/contracts-zero-ex`: I am a bad protocologist Co-authored-by: Lawrence Forman <me@merklejerk.com> * feat: BSC Nerve + Dodo + Nerve + Ellipsis (#181) * feat: BSC Nerve + DODO v1 * CHANGELOGs * Remove extra balance fetch * Add Belt * Added Ellipsis * Update FQT address * `@0x/contracts-zero-ex`: Delete TokenSpenderFeature and get stuff compiling * `@0x/asset-swapper`: fix compilation * prettier * `@0x/asset-swapper`: Truncate LiquidityProvider source ID name * Update packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts Co-authored-by: Jacob Evans <jacob@dekz.net> * Update packages/asset-swapper/src/utils/market_operation_utils/sampler_operations.ts Co-authored-by: Jacob Evans <jacob@dekz.net> * `@0x/contracts-zero-ex`: Fix BakerySwap on PackageSwapFeature (#190) * address review comments Co-authored-by: mzhu25 <mchl.zhu.96@gmail.com> Co-authored-by: Jacob Evans <jacob@dekz.net> Co-authored-by: Lawrence Forman <me@merklejerk.com>
This commit is contained in:
@@ -1,4 +1,33 @@
|
||||
[
|
||||
{
|
||||
"version": "0.21.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Encoding protocol ID and source name in bridge source ID",
|
||||
"pr": 162
|
||||
},
|
||||
{
|
||||
"note": "Add PancakeSwapFeature",
|
||||
"pr": 164
|
||||
},
|
||||
{
|
||||
"note": "Remove TokenSpender/AllowanceTarget/greedy tokens stuff",
|
||||
"pr": 164
|
||||
},
|
||||
{
|
||||
"note": "Added Nerve in BridgeAdapter",
|
||||
"pr": 181
|
||||
},
|
||||
{
|
||||
"note": "Delete TokenSpenderFeature",
|
||||
"pr": 189
|
||||
},
|
||||
{
|
||||
"note": "Fix PancakeSwapFeature BakerySwap swap selector",
|
||||
"pr": 190
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "0.20.0",
|
||||
"changes": [
|
||||
|
@@ -26,6 +26,7 @@ import "./features/interfaces/ITokenSpenderFeature.sol";
|
||||
import "./features/interfaces/ITransformERC20Feature.sol";
|
||||
import "./features/interfaces/IMetaTransactionsFeature.sol";
|
||||
import "./features/interfaces/IUniswapFeature.sol";
|
||||
import "./features/interfaces/IPancakeSwapFeature.sol";
|
||||
import "./features/interfaces/ILiquidityProviderFeature.sol";
|
||||
import "./features/interfaces/INativeOrdersFeature.sol";
|
||||
import "./features/interfaces/IBatchFillNativeOrdersFeature.sol";
|
||||
@@ -36,10 +37,10 @@ import "./features/interfaces/IMultiplexFeature.sol";
|
||||
interface IZeroEx is
|
||||
IOwnableFeature,
|
||||
ISimpleFunctionRegistryFeature,
|
||||
ITokenSpenderFeature,
|
||||
ITransformERC20Feature,
|
||||
IMetaTransactionsFeature,
|
||||
IUniswapFeature,
|
||||
IPancakeSwapFeature,
|
||||
ILiquidityProviderFeature,
|
||||
INativeOrdersFeature,
|
||||
IBatchFillNativeOrdersFeature,
|
||||
|
@@ -1,47 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2020 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6.5;
|
||||
|
||||
|
||||
library LibSpenderRichErrors {
|
||||
|
||||
// solhint-disable func-name-mixedcase
|
||||
|
||||
function SpenderERC20TransferFromFailedError(
|
||||
address token,
|
||||
address owner,
|
||||
address to,
|
||||
uint256 amount,
|
||||
bytes memory errorData
|
||||
)
|
||||
internal
|
||||
pure
|
||||
returns (bytes memory)
|
||||
{
|
||||
return abi.encodeWithSelector(
|
||||
bytes4(keccak256("SpenderERC20TransferFromFailedError(address,address,address,uint256,bytes)")),
|
||||
token,
|
||||
owner,
|
||||
to,
|
||||
amount,
|
||||
errorData
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,56 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2020 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
|
||||
import "@0x/contracts-utils/contracts/src/v06/AuthorizableV06.sol";
|
||||
import "../errors/LibSpenderRichErrors.sol";
|
||||
import "./IAllowanceTarget.sol";
|
||||
|
||||
|
||||
/// @dev The allowance target for the TokenSpender feature.
|
||||
contract AllowanceTarget is
|
||||
IAllowanceTarget,
|
||||
AuthorizableV06
|
||||
{
|
||||
// solhint-disable no-unused-vars,indent,no-empty-blocks
|
||||
using LibRichErrorsV06 for bytes;
|
||||
|
||||
/// @dev Execute an arbitrary call. Only an authority can call this.
|
||||
/// @param target The call target.
|
||||
/// @param callData The call data.
|
||||
/// @return resultData The data returned by the call.
|
||||
function executeCall(
|
||||
address payable target,
|
||||
bytes calldata callData
|
||||
)
|
||||
external
|
||||
override
|
||||
onlyAuthorized
|
||||
returns (bytes memory resultData)
|
||||
{
|
||||
bool success;
|
||||
(success, resultData) = target.call(callData);
|
||||
if (!success) {
|
||||
resultData.rrevert();
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,40 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2020 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/v06/interfaces/IAuthorizableV06.sol";
|
||||
|
||||
|
||||
/// @dev The allowance target for the TokenSpender feature.
|
||||
interface IAllowanceTarget is
|
||||
IAuthorizableV06
|
||||
{
|
||||
/// @dev Execute an arbitrary call. Only an authority can call this.
|
||||
/// @param target The call target.
|
||||
/// @param callData The call data.
|
||||
/// @return resultData The data returned by the call.
|
||||
function executeCall(
|
||||
address payable target,
|
||||
bytes calldata callData
|
||||
)
|
||||
external
|
||||
returns (bytes memory resultData);
|
||||
}
|
@@ -47,15 +47,14 @@ contract LiquidityProviderFeature is
|
||||
/// @dev Name of this feature.
|
||||
string public constant override FEATURE_NAME = "LiquidityProviderFeature";
|
||||
/// @dev Version of this feature.
|
||||
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 3);
|
||||
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 4);
|
||||
|
||||
/// @dev The sandbox contract address.
|
||||
ILiquidityProviderSandbox public immutable sandbox;
|
||||
|
||||
constructor(LiquidityProviderSandbox sandbox_, bytes32 greedyTokensBloomFilter)
|
||||
constructor(LiquidityProviderSandbox sandbox_)
|
||||
public
|
||||
FixinCommon()
|
||||
FixinTokenSpender(greedyTokensBloomFilter)
|
||||
{
|
||||
sandbox = sandbox_;
|
||||
}
|
||||
|
@@ -78,7 +78,7 @@ contract MetaTransactionsFeature is
|
||||
/// @dev Name of this feature.
|
||||
string public constant override FEATURE_NAME = "MetaTransactions";
|
||||
/// @dev Version of this feature.
|
||||
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 1, 0);
|
||||
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 1, 1);
|
||||
/// @dev EIP712 typehash of the `MetaTransactionData` struct.
|
||||
bytes32 public immutable MTX_EIP712_TYPEHASH = keccak256(
|
||||
"MetaTransactionData("
|
||||
@@ -105,11 +105,10 @@ contract MetaTransactionsFeature is
|
||||
}
|
||||
}
|
||||
|
||||
constructor(address zeroExAddress, bytes32 greedyTokensBloomFilter)
|
||||
constructor(address zeroExAddress)
|
||||
public
|
||||
FixinCommon()
|
||||
FixinEIP712(zeroExAddress)
|
||||
FixinTokenSpender(greedyTokensBloomFilter)
|
||||
{
|
||||
// solhint-disable-next-line no-empty-blocks
|
||||
}
|
||||
|
@@ -55,7 +55,7 @@ contract MultiplexFeature is
|
||||
/// @dev Name of this feature.
|
||||
string public constant override FEATURE_NAME = "MultiplexFeature";
|
||||
/// @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 The WETH token contract.
|
||||
IEtherTokenV06 private immutable weth;
|
||||
@@ -73,12 +73,10 @@ contract MultiplexFeature is
|
||||
constructor(
|
||||
address zeroExAddress,
|
||||
IEtherTokenV06 weth_,
|
||||
ILiquidityProviderSandbox sandbox_,
|
||||
bytes32 greedyTokensBloomFilter
|
||||
ILiquidityProviderSandbox sandbox_
|
||||
)
|
||||
public
|
||||
FixinEIP712(zeroExAddress)
|
||||
FixinTokenSpender(greedyTokensBloomFilter)
|
||||
{
|
||||
weth = weth_;
|
||||
sandbox = sandbox_;
|
||||
|
@@ -34,15 +34,14 @@ contract NativeOrdersFeature is
|
||||
/// @dev Name of this feature.
|
||||
string public constant override FEATURE_NAME = "LimitOrders";
|
||||
/// @dev Version of this feature.
|
||||
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 1);
|
||||
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 1, 0);
|
||||
|
||||
constructor(
|
||||
address zeroExAddress,
|
||||
IEtherTokenV06 weth,
|
||||
IStaking staking,
|
||||
FeeCollectorController feeCollectorController,
|
||||
uint32 protocolFeeMultiplier,
|
||||
bytes32 greedyTokensBloomFilter
|
||||
uint32 protocolFeeMultiplier
|
||||
)
|
||||
public
|
||||
NativeOrdersSettlement(
|
||||
@@ -50,8 +49,7 @@ contract NativeOrdersFeature is
|
||||
weth,
|
||||
staking,
|
||||
feeCollectorController,
|
||||
protocolFeeMultiplier,
|
||||
greedyTokensBloomFilter
|
||||
protocolFeeMultiplier
|
||||
)
|
||||
{
|
||||
// solhint-disable no-empty-blocks
|
||||
|
423
contracts/zero-ex/contracts/src/features/PancakeSwapFeature.sol
Normal file
423
contracts/zero-ex/contracts/src/features/PancakeSwapFeature.sol
Normal file
@@ -0,0 +1,423 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2021 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
|
||||
import "../migrations/LibMigrate.sol";
|
||||
import "../fixins/FixinCommon.sol";
|
||||
import "./interfaces/IFeature.sol";
|
||||
import "./interfaces/IPancakeSwapFeature.sol";
|
||||
|
||||
|
||||
/// @dev VIP pancake fill functions.
|
||||
contract PancakeSwapFeature is
|
||||
IFeature,
|
||||
IPancakeSwapFeature,
|
||||
FixinCommon
|
||||
{
|
||||
/// @dev Name of this feature.
|
||||
string public constant override FEATURE_NAME = "PancakeSwapFeature";
|
||||
/// @dev Version of this feature.
|
||||
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 1);
|
||||
/// @dev WBNB contract.
|
||||
IEtherTokenV06 private immutable WBNB;
|
||||
|
||||
// 0xFF + address of the PancakeSwap factory contract.
|
||||
uint256 constant private FF_PANCAKESWAP_FACTORY = 0xffbcfccbde45ce874adcb698cc183debcf179528120000000000000000000000;
|
||||
// 0xFF + address of the BakerySwap factory contract.
|
||||
uint256 constant private FF_BAKERYSWAP_FACTORY = 0xff01bf7c66c6bd861915cdaae475042d3c4bae16a70000000000000000000000;
|
||||
// 0xFF + address of the SushiSwap factory contract.
|
||||
uint256 constant private FF_SUSHISWAP_FACTORY = 0xffc35DADB65012eC5796536bD9864eD8773aBc74C40000000000000000000000;
|
||||
// Init code hash of the PancakeSwap pair contract.
|
||||
uint256 constant private PANCAKESWAP_PAIR_INIT_CODE_HASH = 0xd0d4c4cd0848c93cb4fd1f498d7013ee6bfb25783ea21593d5834f5d250ece66;
|
||||
// Init code hash of the BakerySwap pair contract.
|
||||
uint256 constant private BAKERYSWAP_PAIR_INIT_CODE_HASH = 0xe2e87433120e32c4738a7d8f3271f3d872cbe16241d67537139158d90bac61d3;
|
||||
// Init code hash of the SushiSwap pair contract.
|
||||
uint256 constant private SUSHISWAP_PAIR_INIT_CODE_HASH = 0xe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303;
|
||||
// Mask of the lower 20 bytes of a bytes32.
|
||||
uint256 constant private ADDRESS_MASK = 0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff;
|
||||
// BNB pseudo-token address.
|
||||
uint256 constant private ETH_TOKEN_ADDRESS_32 = 0x000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee;
|
||||
// Maximum token quantity that can be swapped against the PancakeSwapPair contract.
|
||||
uint256 constant private MAX_SWAP_AMOUNT = 2**112;
|
||||
|
||||
// bytes4(keccak256("executeCall(address,bytes)"))
|
||||
uint256 constant private ALLOWANCE_TARGET_EXECUTE_CALL_SELECTOR_32 = 0xbca8c7b500000000000000000000000000000000000000000000000000000000;
|
||||
// bytes4(keccak256("getReserves()"))
|
||||
uint256 constant private PANCAKESWAP_PAIR_RESERVES_CALL_SELECTOR_32 = 0x0902f1ac00000000000000000000000000000000000000000000000000000000;
|
||||
// bytes4(keccak256("swap(uint256,uint256,address,bytes)"))
|
||||
uint256 constant private PANCAKESWAP_PAIR_SWAP_CALL_SELECTOR_32 = 0x022c0d9f00000000000000000000000000000000000000000000000000000000;
|
||||
// bytes4(keccak256("swap(uint256,uint256,address)"))
|
||||
uint256 constant private BAKERYSWAP_PAIR_SWAP_CALL_SELECTOR_32 = 0x6d9a640a00000000000000000000000000000000000000000000000000000000;
|
||||
// bytes4(keccak256("transferFrom(address,address,uint256)"))
|
||||
uint256 constant private TRANSFER_FROM_CALL_SELECTOR_32 = 0x23b872dd00000000000000000000000000000000000000000000000000000000;
|
||||
// bytes4(keccak256("allowance(address,address)"))
|
||||
uint256 constant private ALLOWANCE_CALL_SELECTOR_32 = 0xdd62ed3e00000000000000000000000000000000000000000000000000000000;
|
||||
// bytes4(keccak256("withdraw(uint256)"))
|
||||
uint256 constant private WETH_WITHDRAW_CALL_SELECTOR_32 = 0x2e1a7d4d00000000000000000000000000000000000000000000000000000000;
|
||||
// bytes4(keccak256("deposit()"))
|
||||
uint256 constant private WETH_DEPOSIT_CALL_SELECTOR_32 = 0xd0e30db000000000000000000000000000000000000000000000000000000000;
|
||||
// bytes4(keccak256("transfer(address,uint256)"))
|
||||
uint256 constant private ERC20_TRANSFER_CALL_SELECTOR_32 = 0xa9059cbb00000000000000000000000000000000000000000000000000000000;
|
||||
|
||||
/// @dev Construct this contract.
|
||||
/// @param wbnb The WBNB contract.
|
||||
constructor(IEtherTokenV06 wbnb) public {
|
||||
WBNB = wbnb;
|
||||
}
|
||||
|
||||
/// @dev Initialize and register this feature.
|
||||
/// Should be delegatecalled by `Migrate.migrate()`.
|
||||
/// @return success `LibMigrate.SUCCESS` on success.
|
||||
function migrate()
|
||||
external
|
||||
returns (bytes4 success)
|
||||
{
|
||||
_registerFeatureFunction(this.sellToPancakeSwap.selector);
|
||||
return LibMigrate.MIGRATE_SUCCESS;
|
||||
}
|
||||
|
||||
/// @dev Efficiently sell directly to pancake/BakerySwap/SushiSwap.
|
||||
/// @param tokens Sell path.
|
||||
/// @param sellAmount of `tokens[0]` Amount to sell.
|
||||
/// @param minBuyAmount Minimum amount of `tokens[-1]` to buy.
|
||||
/// @param fork The protocol fork to use.
|
||||
/// @return buyAmount Amount of `tokens[-1]` bought.
|
||||
function sellToPancakeSwap(
|
||||
IERC20TokenV06[] calldata tokens,
|
||||
uint256 sellAmount,
|
||||
uint256 minBuyAmount,
|
||||
ProtocolFork fork
|
||||
)
|
||||
external
|
||||
payable
|
||||
override
|
||||
returns (uint256 buyAmount)
|
||||
{
|
||||
require(tokens.length > 1, "PancakeSwapFeature/InvalidTokensLength");
|
||||
{
|
||||
// Load immutables onto the stack.
|
||||
IEtherTokenV06 wbnb = WBNB;
|
||||
|
||||
// Store some vars in memory to get around stack limits.
|
||||
assembly {
|
||||
// calldataload(mload(0xA00)) == first element of `tokens` array
|
||||
mstore(0xA00, add(calldataload(0x04), 0x24))
|
||||
// mload(0xA20) == fork
|
||||
mstore(0xA20, fork)
|
||||
// mload(0xA40) == WBNB
|
||||
mstore(0xA40, wbnb)
|
||||
}
|
||||
}
|
||||
|
||||
assembly {
|
||||
// numPairs == tokens.length - 1
|
||||
let numPairs := sub(calldataload(add(calldataload(0x04), 0x4)), 1)
|
||||
// We use the previous buy amount as the sell amount for the next
|
||||
// pair in a path. So for the first swap we want to set it to `sellAmount`.
|
||||
buyAmount := sellAmount
|
||||
let buyToken
|
||||
let nextPair := 0
|
||||
|
||||
for {let i := 0} lt(i, numPairs) {i := add(i, 1)} {
|
||||
// sellToken = tokens[i]
|
||||
let sellToken := loadTokenAddress(i)
|
||||
// buyToken = tokens[i+1]
|
||||
buyToken := loadTokenAddress(add(i, 1))
|
||||
// The canonical ordering of this token pair.
|
||||
let pairOrder := lt(normalizeToken(sellToken), normalizeToken(buyToken))
|
||||
|
||||
// Compute the pair address if it hasn't already been computed
|
||||
// from the last iteration.
|
||||
let pair := nextPair
|
||||
if iszero(pair) {
|
||||
pair := computePairAddress(sellToken, buyToken)
|
||||
nextPair := 0
|
||||
}
|
||||
|
||||
if iszero(i) {
|
||||
// This is the first token in the path.
|
||||
switch eq(sellToken, ETH_TOKEN_ADDRESS_32)
|
||||
case 0 { // Not selling BNB. Selling an ERC20 instead.
|
||||
// Make sure BNB was not attached to the call.
|
||||
if gt(callvalue(), 0) {
|
||||
revert(0, 0)
|
||||
}
|
||||
// For the first pair we need to transfer sellTokens into the
|
||||
// pair contract.
|
||||
moveTakerTokensTo(sellToken, pair, sellAmount)
|
||||
}
|
||||
default {
|
||||
// If selling BNB, we need to wrap it to WBNB and transfer to the
|
||||
// pair contract.
|
||||
if iszero(eq(callvalue(), sellAmount)) {
|
||||
revert(0, 0)
|
||||
}
|
||||
sellToken := mload(0xA40)// Re-assign to WBNB
|
||||
// Call `WBNB.deposit{value: sellAmount}()`
|
||||
mstore(0xB00, WETH_DEPOSIT_CALL_SELECTOR_32)
|
||||
if iszero(call(gas(), sellToken, sellAmount, 0xB00, 0x4, 0x00, 0x0)) {
|
||||
bubbleRevert()
|
||||
}
|
||||
// Call `WBNB.transfer(pair, sellAmount)`
|
||||
mstore(0xB00, ERC20_TRANSFER_CALL_SELECTOR_32)
|
||||
mstore(0xB04, pair)
|
||||
mstore(0xB24, sellAmount)
|
||||
if iszero(call(gas(), sellToken, 0, 0xB00, 0x44, 0x00, 0x0)) {
|
||||
bubbleRevert()
|
||||
}
|
||||
}
|
||||
// No need to check results, if deposit/transfers failed the PancakeSwapPair will
|
||||
// reject our trade (or it may succeed if somehow the reserve was out of sync)
|
||||
// this is fine for the taker.
|
||||
}
|
||||
|
||||
// Call pair.getReserves(), store the results at `0xC00`
|
||||
mstore(0xB00, PANCAKESWAP_PAIR_RESERVES_CALL_SELECTOR_32)
|
||||
if iszero(staticcall(gas(), pair, 0xB00, 0x4, 0xC00, 0x40)) {
|
||||
bubbleRevert()
|
||||
}
|
||||
// Revert if the pair contract does not return at least two words.
|
||||
if lt(returndatasize(), 0x40) {
|
||||
mstore(0, pair)
|
||||
revert(0, 32)
|
||||
}
|
||||
|
||||
// Sell amount for this hop is the previous buy amount.
|
||||
let pairSellAmount := buyAmount
|
||||
// Compute the buy amount based on the pair reserves.
|
||||
{
|
||||
let sellReserve
|
||||
let buyReserve
|
||||
switch iszero(pairOrder)
|
||||
case 0 {
|
||||
// Transpose if pair order is different.
|
||||
sellReserve := mload(0xC00)
|
||||
buyReserve := mload(0xC20)
|
||||
}
|
||||
default {
|
||||
sellReserve := mload(0xC20)
|
||||
buyReserve := mload(0xC00)
|
||||
}
|
||||
// Ensure that the sellAmount is < 2¹¹².
|
||||
if gt(pairSellAmount, MAX_SWAP_AMOUNT) {
|
||||
revert(0, 0)
|
||||
}
|
||||
// Pairs are in the range (0, 2¹¹²) so this shouldn't overflow.
|
||||
// buyAmount = (pairSellAmount * 997 * buyReserve) /
|
||||
// (pairSellAmount * 997 + sellReserve * 1000);
|
||||
let sellAmountWithFee := mul(pairSellAmount, 997)
|
||||
buyAmount := div(
|
||||
mul(sellAmountWithFee, buyReserve),
|
||||
add(sellAmountWithFee, mul(sellReserve, 1000))
|
||||
)
|
||||
}
|
||||
|
||||
let receiver
|
||||
// Is this the last pair contract?
|
||||
switch eq(add(i, 1), numPairs)
|
||||
case 0 {
|
||||
// Not the last pair contract, so forward bought tokens to
|
||||
// the next pair contract.
|
||||
nextPair := computePairAddress(
|
||||
buyToken,
|
||||
loadTokenAddress(add(i, 2))
|
||||
)
|
||||
receiver := nextPair
|
||||
}
|
||||
default {
|
||||
// The last pair contract.
|
||||
// Forward directly to taker UNLESS they want BNB back.
|
||||
switch eq(buyToken, ETH_TOKEN_ADDRESS_32)
|
||||
case 0 {
|
||||
receiver := caller()
|
||||
}
|
||||
default {
|
||||
receiver := address()
|
||||
}
|
||||
}
|
||||
|
||||
// Call pair.swap()
|
||||
switch mload(0xA20) // fork
|
||||
case 1 {
|
||||
mstore(0xB00, BAKERYSWAP_PAIR_SWAP_CALL_SELECTOR_32)
|
||||
}
|
||||
default {
|
||||
mstore(0xB00, PANCAKESWAP_PAIR_SWAP_CALL_SELECTOR_32)
|
||||
}
|
||||
switch pairOrder
|
||||
case 0 {
|
||||
mstore(0xB04, buyAmount)
|
||||
mstore(0xB24, 0)
|
||||
}
|
||||
default {
|
||||
mstore(0xB04, 0)
|
||||
mstore(0xB24, buyAmount)
|
||||
}
|
||||
mstore(0xB44, receiver)
|
||||
mstore(0xB64, 0x80)
|
||||
mstore(0xB84, 0)
|
||||
if iszero(call(gas(), pair, 0, 0xB00, 0xA4, 0, 0)) {
|
||||
bubbleRevert()
|
||||
}
|
||||
} // End for-loop.
|
||||
|
||||
// If buying BNB, unwrap the WBNB first
|
||||
if eq(buyToken, ETH_TOKEN_ADDRESS_32) {
|
||||
// Call `WBNB.withdraw(buyAmount)`
|
||||
mstore(0xB00, WETH_WITHDRAW_CALL_SELECTOR_32)
|
||||
mstore(0xB04, buyAmount)
|
||||
if iszero(call(gas(), mload(0xA40), 0, 0xB00, 0x24, 0x00, 0x0)) {
|
||||
bubbleRevert()
|
||||
}
|
||||
// Transfer BNB to the caller.
|
||||
if iszero(call(gas(), caller(), buyAmount, 0xB00, 0x0, 0x00, 0x0)) {
|
||||
bubbleRevert()
|
||||
}
|
||||
}
|
||||
|
||||
// Functions ///////////////////////////////////////////////////////
|
||||
|
||||
// Load a token address from the `tokens` calldata argument.
|
||||
function loadTokenAddress(idx) -> addr {
|
||||
addr := and(ADDRESS_MASK, calldataload(add(mload(0xA00), mul(idx, 0x20))))
|
||||
}
|
||||
|
||||
// Convert BNB pseudo-token addresses to WBNB.
|
||||
function normalizeToken(token) -> normalized {
|
||||
normalized := token
|
||||
// Translate BNB pseudo-tokens to WBNB.
|
||||
if eq(token, ETH_TOKEN_ADDRESS_32) {
|
||||
normalized := mload(0xA40)
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the address of the PancakeSwapPair contract given two
|
||||
// tokens.
|
||||
function computePairAddress(tokenA, tokenB) -> pair {
|
||||
// Convert BNB pseudo-token addresses to WBNB.
|
||||
tokenA := normalizeToken(tokenA)
|
||||
tokenB := normalizeToken(tokenB)
|
||||
// There is one contract for every combination of tokens,
|
||||
// which is deployed using CREATE2.
|
||||
// The derivation of this address is given by:
|
||||
// address(keccak256(abi.encodePacked(
|
||||
// bytes(0xFF),
|
||||
// address(PANCAKESWAP_FACTORY_ADDRESS),
|
||||
// keccak256(abi.encodePacked(
|
||||
// tokenA < tokenB ? tokenA : tokenB,
|
||||
// tokenA < tokenB ? tokenB : tokenA,
|
||||
// )),
|
||||
// bytes32(PANCAKESWAP_PAIR_INIT_CODE_HASH),
|
||||
// )));
|
||||
|
||||
// Compute the salt (the hash of the sorted tokens).
|
||||
// Tokens are written in reverse memory order to packed encode
|
||||
// them as two 20-byte values in a 40-byte chunk of memory
|
||||
// starting at 0xB0C.
|
||||
switch lt(tokenA, tokenB)
|
||||
case 0 {
|
||||
mstore(0xB14, tokenA)
|
||||
mstore(0xB00, tokenB)
|
||||
}
|
||||
default {
|
||||
mstore(0xB14, tokenB)
|
||||
mstore(0xB00, tokenA)
|
||||
}
|
||||
let salt := keccak256(0xB0C, 0x28)
|
||||
// Compute the pair address by hashing all the components together.
|
||||
switch mload(0xA20) // fork
|
||||
case 0 {
|
||||
mstore(0xB00, FF_PANCAKESWAP_FACTORY)
|
||||
mstore(0xB15, salt)
|
||||
mstore(0xB35, PANCAKESWAP_PAIR_INIT_CODE_HASH)
|
||||
}
|
||||
case 1 {
|
||||
mstore(0xB00, FF_BAKERYSWAP_FACTORY)
|
||||
mstore(0xB15, salt)
|
||||
mstore(0xB35, BAKERYSWAP_PAIR_INIT_CODE_HASH)
|
||||
}
|
||||
default {
|
||||
mstore(0xB00, FF_SUSHISWAP_FACTORY)
|
||||
mstore(0xB15, salt)
|
||||
mstore(0xB35, SUSHISWAP_PAIR_INIT_CODE_HASH)
|
||||
}
|
||||
pair := and(ADDRESS_MASK, keccak256(0xB00, 0x55))
|
||||
}
|
||||
|
||||
// Revert with the return data from the most recent call.
|
||||
function bubbleRevert() {
|
||||
returndatacopy(0, 0, returndatasize())
|
||||
revert(0, returndatasize())
|
||||
}
|
||||
|
||||
// Move `amount` tokens from the taker/caller to `to`.
|
||||
function moveTakerTokensTo(token, to, amount) {
|
||||
// Perform a `transferFrom()`
|
||||
mstore(0xB00, TRANSFER_FROM_CALL_SELECTOR_32)
|
||||
mstore(0xB04, caller())
|
||||
mstore(0xB24, to)
|
||||
mstore(0xB44, amount)
|
||||
|
||||
let success := call(
|
||||
gas(),
|
||||
token,
|
||||
0,
|
||||
0xB00,
|
||||
0x64,
|
||||
0xC00,
|
||||
// Copy only the first 32 bytes of return data. We
|
||||
// only care about reading a boolean in the success
|
||||
// case. We will use returndatacopy() in the failure case.
|
||||
0x20
|
||||
)
|
||||
|
||||
let rdsize := returndatasize()
|
||||
|
||||
// Check for ERC20 success. ERC20 tokens should
|
||||
// return a boolean, but some return nothing or
|
||||
// extra data. We accept 0-length return data as
|
||||
// success, or at least 32 bytes that starts with
|
||||
// a 32-byte boolean true.
|
||||
success := and(
|
||||
success, // call itself succeeded
|
||||
or(
|
||||
iszero(rdsize), // no return data, or
|
||||
and(
|
||||
iszero(lt(rdsize, 32)), // at least 32 bytes
|
||||
eq(mload(0xC00), 1) // starts with uint256(1)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
if iszero(success) {
|
||||
// Revert with the data returned from the transferFrom call.
|
||||
returndatacopy(0, 0, rdsize)
|
||||
revert(0, rdsize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Revert if we bought too little.
|
||||
require(buyAmount >= minBuyAmount, "PancakeSwapFeature/UnderBought");
|
||||
}
|
||||
}
|
@@ -1,137 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2020 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
|
||||
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
|
||||
import "../errors/LibSpenderRichErrors.sol";
|
||||
import "../fixins/FixinCommon.sol";
|
||||
import "../migrations/LibMigrate.sol";
|
||||
import "../external/IAllowanceTarget.sol";
|
||||
import "../storage/LibTokenSpenderStorage.sol";
|
||||
import "./interfaces/IFeature.sol";
|
||||
import "./interfaces/ITokenSpenderFeature.sol";
|
||||
|
||||
|
||||
/// @dev Feature that allows spending token allowances.
|
||||
contract TokenSpenderFeature is
|
||||
IFeature,
|
||||
ITokenSpenderFeature,
|
||||
FixinCommon
|
||||
{
|
||||
// solhint-disable
|
||||
/// @dev Name of this feature.
|
||||
string public constant override FEATURE_NAME = "TokenSpender";
|
||||
/// @dev Version of this feature.
|
||||
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 0);
|
||||
// solhint-enable
|
||||
|
||||
using LibRichErrorsV06 for bytes;
|
||||
|
||||
/// @dev Initialize and register this feature. Should be delegatecalled
|
||||
/// into during a `Migrate.migrate()`.
|
||||
/// @param allowanceTarget An `allowanceTarget` instance, configured to have
|
||||
/// the ZeroeEx contract as an authority.
|
||||
/// @return success `MIGRATE_SUCCESS` on success.
|
||||
function migrate(IAllowanceTarget allowanceTarget)
|
||||
external
|
||||
returns (bytes4 success)
|
||||
{
|
||||
LibTokenSpenderStorage.getStorage().allowanceTarget = allowanceTarget;
|
||||
_registerFeatureFunction(this.getAllowanceTarget.selector);
|
||||
_registerFeatureFunction(this._spendERC20Tokens.selector);
|
||||
_registerFeatureFunction(this.getSpendableERC20BalanceOf.selector);
|
||||
return LibMigrate.MIGRATE_SUCCESS;
|
||||
}
|
||||
|
||||
/// @dev Transfers ERC20 tokens from `owner` to `to`. Only callable from within.
|
||||
/// @param token The token to spend.
|
||||
/// @param owner The owner of the tokens.
|
||||
/// @param to The recipient of the tokens.
|
||||
/// @param amount The amount of `token` to transfer.
|
||||
function _spendERC20Tokens(
|
||||
IERC20TokenV06 token,
|
||||
address owner,
|
||||
address to,
|
||||
uint256 amount
|
||||
)
|
||||
external
|
||||
override
|
||||
onlySelf
|
||||
{
|
||||
IAllowanceTarget spender = LibTokenSpenderStorage.getStorage().allowanceTarget;
|
||||
// Have the allowance target execute an ERC20 `transferFrom()`.
|
||||
(bool didSucceed, bytes memory resultData) = address(spender).call(
|
||||
abi.encodeWithSelector(
|
||||
IAllowanceTarget.executeCall.selector,
|
||||
address(token),
|
||||
abi.encodeWithSelector(
|
||||
IERC20TokenV06.transferFrom.selector,
|
||||
owner,
|
||||
to,
|
||||
amount
|
||||
)
|
||||
)
|
||||
);
|
||||
if (didSucceed) {
|
||||
resultData = abi.decode(resultData, (bytes));
|
||||
}
|
||||
if (!didSucceed || !LibERC20TokenV06.isSuccessfulResult(resultData)) {
|
||||
LibSpenderRichErrors.SpenderERC20TransferFromFailedError(
|
||||
address(token),
|
||||
owner,
|
||||
to,
|
||||
amount,
|
||||
resultData
|
||||
).rrevert();
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Gets the maximum amount of an ERC20 token `token` that can be
|
||||
/// pulled from `owner` by the token spender.
|
||||
/// @param token The token to spend.
|
||||
/// @param owner The owner of the tokens.
|
||||
/// @return amount The amount of tokens that can be pulled.
|
||||
function getSpendableERC20BalanceOf(IERC20TokenV06 token, address owner)
|
||||
external
|
||||
override
|
||||
view
|
||||
returns (uint256 amount)
|
||||
{
|
||||
return LibSafeMathV06.min256(
|
||||
token.allowance(owner, address(LibTokenSpenderStorage.getStorage().allowanceTarget)),
|
||||
token.balanceOf(owner)
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Get the address of the allowance target.
|
||||
/// @return target The target of token allowances.
|
||||
function getAllowanceTarget()
|
||||
external
|
||||
override
|
||||
view
|
||||
returns (address target)
|
||||
{
|
||||
return address(LibTokenSpenderStorage.getStorage().allowanceTarget);
|
||||
}
|
||||
}
|
@@ -60,10 +60,7 @@ contract TransformERC20Feature is
|
||||
/// @dev Version of this feature.
|
||||
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 3, 1);
|
||||
|
||||
constructor(bytes32 greedyTokensBloomFilter)
|
||||
public
|
||||
FixinTokenSpender(greedyTokensBloomFilter)
|
||||
{}
|
||||
constructor() public {}
|
||||
|
||||
/// @dev Initialize and register this feature.
|
||||
/// Should be delegatecalled by `Migrate.migrate()`.
|
||||
|
@@ -23,7 +23,6 @@ pragma experimental ABIEncoderV2;
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
|
||||
import "../migrations/LibMigrate.sol";
|
||||
import "../external/IAllowanceTarget.sol";
|
||||
import "../fixins/FixinCommon.sol";
|
||||
import "./interfaces/IFeature.sol";
|
||||
import "./interfaces/IUniswapFeature.sol";
|
||||
@@ -38,13 +37,9 @@ contract UniswapFeature is
|
||||
/// @dev Name of this feature.
|
||||
string public constant override FEATURE_NAME = "UniswapFeature";
|
||||
/// @dev Version of this feature.
|
||||
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 1, 1);
|
||||
/// @dev A bloom filter for tokens that consume all gas when `transferFrom()` fails.
|
||||
bytes32 public immutable GREEDY_TOKENS_BLOOM_FILTER;
|
||||
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 1, 2);
|
||||
/// @dev WETH contract.
|
||||
IEtherTokenV06 private immutable WETH;
|
||||
/// @dev AllowanceTarget instance.
|
||||
IAllowanceTarget private immutable ALLOWANCE_TARGET;
|
||||
|
||||
// 0xFF + address of the UniswapV2Factory contract.
|
||||
uint256 constant private FF_UNISWAP_FACTORY = 0xFF5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f0000000000000000000000;
|
||||
@@ -80,16 +75,8 @@ contract UniswapFeature is
|
||||
|
||||
/// @dev Construct this contract.
|
||||
/// @param weth The WETH contract.
|
||||
/// @param allowanceTarget The AllowanceTarget contract.
|
||||
/// @param greedyTokensBloomFilter The bloom filter for greedy tokens.
|
||||
constructor(
|
||||
IEtherTokenV06 weth,
|
||||
IAllowanceTarget allowanceTarget,
|
||||
bytes32 greedyTokensBloomFilter
|
||||
) public {
|
||||
constructor(IEtherTokenV06 weth) public {
|
||||
WETH = weth;
|
||||
ALLOWANCE_TARGET = allowanceTarget;
|
||||
GREEDY_TOKENS_BLOOM_FILTER = greedyTokensBloomFilter;
|
||||
}
|
||||
|
||||
/// @dev Initialize and register this feature.
|
||||
@@ -124,8 +111,6 @@ contract UniswapFeature is
|
||||
{
|
||||
// Load immutables onto the stack.
|
||||
IEtherTokenV06 weth = WETH;
|
||||
IAllowanceTarget allowanceTarget = ALLOWANCE_TARGET;
|
||||
bytes32 greedyTokensBloomFilter = GREEDY_TOKENS_BLOOM_FILTER;
|
||||
|
||||
// Store some vars in memory to get around stack limits.
|
||||
assembly {
|
||||
@@ -135,10 +120,6 @@ contract UniswapFeature is
|
||||
mstore(0xA20, isSushi)
|
||||
// mload(0xA40) == WETH
|
||||
mstore(0xA40, weth)
|
||||
// mload(0xA60) == ALLOWANCE_TARGET
|
||||
mstore(0xA60, allowanceTarget)
|
||||
// mload(0xA80) == GREEDY_TOKENS_BLOOM_FILTER
|
||||
mstore(0xA80, greedyTokensBloomFilter)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -373,38 +354,7 @@ contract UniswapFeature is
|
||||
|
||||
// Move `amount` tokens from the taker/caller to `to`.
|
||||
function moveTakerTokensTo(token, to, amount) {
|
||||
|
||||
// If the token is possibly greedy, we check the allowance rather
|
||||
// than relying on letting the transferFrom() call fail and
|
||||
// falling through to legacy allowance target because the token
|
||||
// will eat all our gas.
|
||||
if isTokenPossiblyGreedy(token) {
|
||||
// Check if we have enough direct allowance by calling
|
||||
// `token.allowance()`
|
||||
mstore(0xB00, ALLOWANCE_CALL_SELECTOR_32)
|
||||
mstore(0xB04, caller())
|
||||
mstore(0xB24, address())
|
||||
let success := staticcall(gas(), token, 0xB00, 0x44, 0xC00, 0x20)
|
||||
if iszero(success) {
|
||||
// Call to allowance() failed.
|
||||
bubbleRevert()
|
||||
}
|
||||
// Make sure the allowance call returned at least a word.
|
||||
if lt(returndatasize(), 0x20) {
|
||||
revert(0, 0)
|
||||
}
|
||||
// Call succeeded.
|
||||
// Result is stored in 0xC00-0xC20.
|
||||
if lt(mload(0xC00), amount) {
|
||||
// We don't have enough direct allowance, so try
|
||||
// going through the legacy allowance taregt.
|
||||
moveTakerTokensToWithLegacyAllowanceTarget(token, to, amount)
|
||||
leave
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise we will optimistically try to perform a `transferFrom()`
|
||||
// directly then if it fails we will go through the legacy allowance target.
|
||||
// Perform a `transferFrom()`
|
||||
mstore(0xB00, TRANSFER_FROM_CALL_SELECTOR_32)
|
||||
mstore(0xB04, caller())
|
||||
mstore(0xB24, to)
|
||||
@@ -419,8 +369,7 @@ contract UniswapFeature is
|
||||
0xC00,
|
||||
// Copy only the first 32 bytes of return data. We
|
||||
// only care about reading a boolean in the success
|
||||
// case, and we discard the return data in the
|
||||
// failure case.
|
||||
// case. We will use returndatacopy() in the failure case.
|
||||
0x20
|
||||
)
|
||||
|
||||
@@ -443,37 +392,11 @@ contract UniswapFeature is
|
||||
)
|
||||
|
||||
if iszero(success) {
|
||||
// Try to fall back to the allowance target.
|
||||
moveTakerTokensToWithLegacyAllowanceTarget(token, to, amount)
|
||||
// Revert with the data returned from the transferFrom call.
|
||||
returndatacopy(0, 0, rdsize)
|
||||
revert(0, rdsize)
|
||||
}
|
||||
}
|
||||
|
||||
// Move tokens by going through the legacy allowance target contract.
|
||||
function moveTakerTokensToWithLegacyAllowanceTarget(token, to, amount) {
|
||||
mstore(0xB00, ALLOWANCE_TARGET_EXECUTE_CALL_SELECTOR_32)
|
||||
mstore(0xB04, token)
|
||||
mstore(0xB24, 0x40)
|
||||
mstore(0xB44, 0x64)
|
||||
mstore(0xB64, TRANSFER_FROM_CALL_SELECTOR_32)
|
||||
mstore(0xB68, caller())
|
||||
mstore(0xB88, to)
|
||||
mstore(0xBA8, amount)
|
||||
if iszero(call(gas(), mload(0xA60), 0, 0xB00, 0xC8, 0x00, 0x0)) {
|
||||
bubbleRevert()
|
||||
}
|
||||
// If this fall back failed, the swap will most likely fail
|
||||
// so there's no need to validate the result.
|
||||
}
|
||||
|
||||
// Checks if a token possibly belongs to the GREEDY_TOKENS_BLOOM_FILTER
|
||||
// bloom filter.
|
||||
function isTokenPossiblyGreedy(token) -> isPossiblyGreedy {
|
||||
// The hash is given by:
|
||||
// (1 << (keccak256(token) % 256)) | (1 << (token % 256))
|
||||
mstore(0, token)
|
||||
let h := or(shl(mod(keccak256(0, 32), 256), 1), shl(mod(token, 256), 1))
|
||||
isPossiblyGreedy := eq(and(h, mload(0xA80)), h)
|
||||
}
|
||||
}
|
||||
|
||||
// Revert if we bought too little.
|
||||
|
@@ -0,0 +1,50 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2021 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||
|
||||
|
||||
/// @dev VIP PancakeSwap/BakerySwap/SushiSwap fill functions.
|
||||
interface IPancakeSwapFeature {
|
||||
|
||||
enum ProtocolFork {
|
||||
PancakeSwap,
|
||||
BakerySwap,
|
||||
SushiSwap
|
||||
}
|
||||
|
||||
/// @dev Efficiently sell directly to PancakeSwap/BakerySwap/Sushiswap.
|
||||
/// @param tokens Sell path.
|
||||
/// @param sellAmount of `tokens[0]` Amount to sell.
|
||||
/// @param minBuyAmount Minimum amount of `tokens[-1]` to buy.
|
||||
/// @param fork The protocol fork to use.
|
||||
/// @return buyAmount Amount of `tokens[-1]` bought.
|
||||
function sellToPancakeSwap(
|
||||
IERC20TokenV06[] calldata tokens,
|
||||
uint256 sellAmount,
|
||||
uint256 minBuyAmount,
|
||||
ProtocolFork fork
|
||||
)
|
||||
external
|
||||
payable
|
||||
returns (uint256 buyAmount);
|
||||
}
|
@@ -40,11 +40,10 @@ abstract contract NativeOrdersCancellation is
|
||||
uint256 private constant HIGH_BIT = 1 << 255;
|
||||
|
||||
constructor(
|
||||
address zeroExAddress,
|
||||
bytes32 greedyTokensBloomFilter
|
||||
address zeroExAddress
|
||||
)
|
||||
internal
|
||||
NativeOrdersInfo(zeroExAddress, greedyTokensBloomFilter)
|
||||
NativeOrdersInfo(zeroExAddress)
|
||||
{
|
||||
// solhint-disable no-empty-blocks
|
||||
}
|
||||
|
@@ -51,12 +51,10 @@ abstract contract NativeOrdersInfo is
|
||||
uint256 private constant HIGH_BIT = 1 << 255;
|
||||
|
||||
constructor(
|
||||
address zeroExAddress,
|
||||
bytes32 greedyTokensBloomFilter
|
||||
address zeroExAddress
|
||||
)
|
||||
internal
|
||||
FixinEIP712(zeroExAddress)
|
||||
FixinTokenSpender(greedyTokensBloomFilter)
|
||||
{
|
||||
// solhint-disable no-empty-blocks
|
||||
}
|
||||
|
@@ -96,11 +96,10 @@ abstract contract NativeOrdersSettlement is
|
||||
IEtherTokenV06 weth,
|
||||
IStaking staking,
|
||||
FeeCollectorController feeCollectorController,
|
||||
uint32 protocolFeeMultiplier,
|
||||
bytes32 greedyTokensBloomFilter
|
||||
uint32 protocolFeeMultiplier
|
||||
)
|
||||
public
|
||||
NativeOrdersCancellation(zeroExAddress, greedyTokensBloomFilter)
|
||||
NativeOrdersCancellation(zeroExAddress)
|
||||
NativeOrdersProtocolFees(weth, staking, feeCollectorController, protocolFeeMultiplier)
|
||||
{
|
||||
// solhint-disable no-empty-blocks
|
||||
|
@@ -22,28 +22,13 @@ pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
|
||||
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
|
||||
import "../features/interfaces/ITokenSpenderFeature.sol";
|
||||
import "../errors/LibSpenderRichErrors.sol";
|
||||
import "../external/FeeCollector.sol";
|
||||
import "../vendor/v3/IStaking.sol";
|
||||
import "../vendor/v3/IStaking.sol";
|
||||
|
||||
|
||||
/// @dev Helpers for moving tokens around.
|
||||
abstract contract FixinTokenSpender {
|
||||
using LibRichErrorsV06 for bytes;
|
||||
|
||||
// Mask of the lower 20 bytes of a bytes32.
|
||||
uint256 constant private ADDRESS_MASK = 0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff;
|
||||
/// @dev A bloom filter for tokens that consume all gas when `transferFrom()` fails.
|
||||
bytes32 public immutable GREEDY_TOKENS_BLOOM_FILTER;
|
||||
|
||||
/// @param greedyTokensBloomFilter The bloom filter for all greedy tokens.
|
||||
constructor(bytes32 greedyTokensBloomFilter)
|
||||
internal
|
||||
{
|
||||
GREEDY_TOKENS_BLOOM_FILTER = greedyTokensBloomFilter;
|
||||
}
|
||||
|
||||
/// @dev Transfers ERC20 tokens from `owner` to `to`.
|
||||
/// @param token The token to spend.
|
||||
@@ -58,29 +43,8 @@ abstract contract FixinTokenSpender {
|
||||
)
|
||||
internal
|
||||
{
|
||||
bool success;
|
||||
bytes memory revertData;
|
||||
|
||||
require(address(token) != address(this), "FixinTokenSpender/CANNOT_INVOKE_SELF");
|
||||
|
||||
// If the token eats all gas when failing, we do not want to perform
|
||||
// optimistic fall through to the old AllowanceTarget contract if the
|
||||
// direct transferFrom() fails.
|
||||
if (_isTokenPossiblyGreedy(token)) {
|
||||
// If the token does not have a direct allowance on us then we use
|
||||
// the allowance target.
|
||||
if (token.allowance(owner, address(this)) < amount) {
|
||||
_transferFromLegacyAllowanceTarget(
|
||||
token,
|
||||
owner,
|
||||
to,
|
||||
amount,
|
||||
""
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
assembly {
|
||||
let ptr := mload(0x40) // free memory pointer
|
||||
|
||||
@@ -90,20 +54,18 @@ abstract contract FixinTokenSpender {
|
||||
mstore(add(ptr, 0x24), and(to, ADDRESS_MASK))
|
||||
mstore(add(ptr, 0x44), amount)
|
||||
|
||||
success := call(
|
||||
let success := call(
|
||||
gas(),
|
||||
and(token, ADDRESS_MASK),
|
||||
0,
|
||||
ptr,
|
||||
0x64,
|
||||
0,
|
||||
0
|
||||
ptr,
|
||||
32
|
||||
)
|
||||
|
||||
let rdsize := returndatasize()
|
||||
|
||||
returndatacopy(add(ptr, 0x20), 0, rdsize) // reuse memory
|
||||
|
||||
// Check for ERC20 success. ERC20 tokens should return a boolean,
|
||||
// but some don't. We accept 0-length return data as success, or at
|
||||
// least 32 bytes that starts with a 32-byte boolean true.
|
||||
@@ -113,30 +75,16 @@ abstract contract FixinTokenSpender {
|
||||
iszero(rdsize), // no return data, or
|
||||
and(
|
||||
iszero(lt(rdsize, 32)), // at least 32 bytes
|
||||
eq(mload(add(ptr, 0x20)), 1) // starts with uint256(1)
|
||||
eq(mload(ptr), 1) // starts with uint256(1)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
if iszero(success) {
|
||||
// revertData is a bytes, so length-prefixed data
|
||||
mstore(ptr, rdsize)
|
||||
revertData := ptr
|
||||
|
||||
// update free memory pointer (ptr + 32-byte length + return data)
|
||||
mstore(0x40, add(add(ptr, 0x20), rdsize))
|
||||
returndatacopy(ptr, 0, rdsize)
|
||||
revert(ptr, rdsize)
|
||||
}
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
_transferFromLegacyAllowanceTarget(
|
||||
token,
|
||||
owner,
|
||||
to,
|
||||
amount,
|
||||
revertData
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Gets the maximum amount of an ERC20 token `token` that can be
|
||||
@@ -157,53 +105,4 @@ abstract contract FixinTokenSpender {
|
||||
token.balanceOf(owner)
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Check if a token possibly belongs to the `GREEDY_TOKENS_BLOOM_FILTER`
|
||||
/// bloom filter.
|
||||
function _isTokenPossiblyGreedy(IERC20TokenV06 token)
|
||||
internal
|
||||
view
|
||||
returns (bool isPossiblyGreedy)
|
||||
{
|
||||
// The hash is given by:
|
||||
// (1 << (keccak256(token) % 256)) | (1 << (token % 256))
|
||||
bytes32 h;
|
||||
assembly {
|
||||
mstore(0, token)
|
||||
h := or(shl(mod(keccak256(0, 32), 256), 1), shl(mod(token, 256), 1))
|
||||
}
|
||||
return (h & GREEDY_TOKENS_BLOOM_FILTER) == h;
|
||||
}
|
||||
|
||||
/// @dev Transfer tokens using the legacy allowance target instead of
|
||||
/// allowances directly set on the exchange proxy.
|
||||
function _transferFromLegacyAllowanceTarget(
|
||||
IERC20TokenV06 token,
|
||||
address owner,
|
||||
address to,
|
||||
uint256 amount,
|
||||
bytes memory initialRevertData
|
||||
)
|
||||
private
|
||||
{
|
||||
// Try the old AllowanceTarget.
|
||||
try ITokenSpenderFeature(address(this))._spendERC20Tokens(
|
||||
token,
|
||||
owner,
|
||||
to,
|
||||
amount
|
||||
) {
|
||||
} catch (bytes memory revertData) {
|
||||
// Bubble up the first error message. (In general, the fallback to the
|
||||
// allowance target is opportunistic. We ignore the specific error
|
||||
// message if it fails.)
|
||||
LibSpenderRichErrors.SpenderERC20TransferFromFailedError(
|
||||
address(token),
|
||||
owner,
|
||||
to,
|
||||
amount,
|
||||
initialRevertData.length != 0 ? initialRevertData : revertData
|
||||
).rrevert();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -22,11 +22,9 @@ pragma experimental ABIEncoderV2;
|
||||
|
||||
import "../ZeroEx.sol";
|
||||
import "../features/interfaces/IOwnableFeature.sol";
|
||||
import "../features/TokenSpenderFeature.sol";
|
||||
import "../features/TransformERC20Feature.sol";
|
||||
import "../features/MetaTransactionsFeature.sol";
|
||||
import "../features/NativeOrdersFeature.sol";
|
||||
import "../external/AllowanceTarget.sol";
|
||||
import "./InitialMigration.sol";
|
||||
|
||||
|
||||
@@ -39,7 +37,6 @@ contract FullMigration {
|
||||
struct Features {
|
||||
SimpleFunctionRegistryFeature registry;
|
||||
OwnableFeature ownable;
|
||||
TokenSpenderFeature tokenSpender;
|
||||
TransformERC20Feature transformERC20;
|
||||
MetaTransactionsFeature metaTransactions;
|
||||
NativeOrdersFeature nativeOrders;
|
||||
@@ -107,7 +104,7 @@ contract FullMigration {
|
||||
);
|
||||
|
||||
// Add features.
|
||||
_addFeatures(zeroEx, owner, features, migrateOpts);
|
||||
_addFeatures(zeroEx, features, migrateOpts);
|
||||
|
||||
// Transfer ownership to the real owner.
|
||||
IOwnableFeature(address(zeroEx)).transferOwnership(owner);
|
||||
@@ -132,36 +129,16 @@ contract FullMigration {
|
||||
|
||||
/// @dev Deploy and register features to the ZeroEx contract.
|
||||
/// @param zeroEx The bootstrapped ZeroEx contract.
|
||||
/// @param owner The ultimate owner of the ZeroEx contract.
|
||||
/// @param features Features to add to the proxy.
|
||||
/// @param migrateOpts Parameters needed to initialize features.
|
||||
function _addFeatures(
|
||||
ZeroEx zeroEx,
|
||||
address owner,
|
||||
Features memory features,
|
||||
MigrateOpts memory migrateOpts
|
||||
)
|
||||
private
|
||||
{
|
||||
IOwnableFeature ownable = IOwnableFeature(address(zeroEx));
|
||||
// TokenSpenderFeature
|
||||
{
|
||||
// Create the allowance target.
|
||||
AllowanceTarget allowanceTarget = new AllowanceTarget();
|
||||
// Let the ZeroEx contract use the allowance target.
|
||||
allowanceTarget.addAuthorizedAddress(address(zeroEx));
|
||||
// Transfer ownership of the allowance target to the (real) owner.
|
||||
allowanceTarget.transferOwnership(owner);
|
||||
// Register the feature.
|
||||
ownable.migrate(
|
||||
address(features.tokenSpender),
|
||||
abi.encodeWithSelector(
|
||||
TokenSpenderFeature.migrate.selector,
|
||||
allowanceTarget
|
||||
),
|
||||
address(this)
|
||||
);
|
||||
}
|
||||
// TransformERC20Feature
|
||||
{
|
||||
// Register the feature.
|
||||
|
@@ -1,46 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2020 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "./LibStorage.sol";
|
||||
import "../external/IAllowanceTarget.sol";
|
||||
|
||||
|
||||
/// @dev Storage helpers for the `TokenSpender` feature.
|
||||
library LibTokenSpenderStorage {
|
||||
|
||||
/// @dev Storage bucket for this feature.
|
||||
struct Storage {
|
||||
// Allowance target contract.
|
||||
IAllowanceTarget allowanceTarget;
|
||||
}
|
||||
|
||||
/// @dev Get the storage bucket for this contract.
|
||||
function getStorage() internal pure returns (Storage storage stor) {
|
||||
uint256 storageSlot = LibStorage.getStorageSlot(
|
||||
LibStorage.StorageId.TokenSpender
|
||||
);
|
||||
// Dip into assembly to change the slot pointed to by the local
|
||||
// variable `stor`.
|
||||
// See https://solidity.readthedocs.io/en/v0.6.8/assembly.html?highlight=slot#access-to-external-variables-functions-and-libraries
|
||||
assembly { stor_slot := storageSlot }
|
||||
}
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2020 ZeroEx Intl.
|
||||
Copyright 2021 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -21,7 +21,7 @@ pragma solidity ^0.6.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "./IBridgeAdapter.sol";
|
||||
import "./BridgeSource.sol";
|
||||
import "./BridgeProtocols.sol";
|
||||
import "./mixins/MixinBalancer.sol";
|
||||
import "./mixins/MixinBancor.sol";
|
||||
import "./mixins/MixinCoFiX.sol";
|
||||
@@ -32,9 +32,9 @@ import "./mixins/MixinDodoV2.sol";
|
||||
import "./mixins/MixinKyber.sol";
|
||||
import "./mixins/MixinMooniswap.sol";
|
||||
import "./mixins/MixinMStable.sol";
|
||||
import "./mixins/MixinNerve.sol";
|
||||
import "./mixins/MixinOasis.sol";
|
||||
import "./mixins/MixinShell.sol";
|
||||
import "./mixins/MixinSushiswap.sol";
|
||||
import "./mixins/MixinUniswap.sol";
|
||||
import "./mixins/MixinUniswapV2.sol";
|
||||
import "./mixins/MixinZeroExBridge.sol";
|
||||
@@ -51,9 +51,9 @@ contract BridgeAdapter is
|
||||
MixinKyber,
|
||||
MixinMooniswap,
|
||||
MixinMStable,
|
||||
MixinNerve,
|
||||
MixinOasis,
|
||||
MixinShell,
|
||||
MixinSushiswap,
|
||||
MixinUniswap,
|
||||
MixinUniswapV2,
|
||||
MixinZeroExBridge
|
||||
@@ -70,9 +70,9 @@ contract BridgeAdapter is
|
||||
MixinKyber(weth)
|
||||
MixinMooniswap(weth)
|
||||
MixinMStable()
|
||||
MixinNerve()
|
||||
MixinOasis()
|
||||
MixinShell()
|
||||
MixinSushiswap()
|
||||
MixinUniswap(weth)
|
||||
MixinUniswapV2()
|
||||
MixinZeroExBridge()
|
||||
@@ -88,109 +88,106 @@ contract BridgeAdapter is
|
||||
override
|
||||
returns (uint256 boughtAmount)
|
||||
{
|
||||
if (order.source == BridgeSource.CURVE ||
|
||||
order.source == BridgeSource.SWERVE ||
|
||||
order.source == BridgeSource.SNOWSWAP) {
|
||||
uint128 protocolId = uint128(uint256(order.source) >> 128);
|
||||
if (protocolId == BridgeProtocols.CURVE) {
|
||||
boughtAmount = _tradeCurve(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (order.source == BridgeSource.SUSHISWAP) {
|
||||
boughtAmount = _tradeSushiswap(
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (order.source == BridgeSource.UNISWAPV2 ||
|
||||
order.source == BridgeSource.LINKSWAP) {
|
||||
} else if (protocolId == BridgeProtocols.UNISWAPV2) {
|
||||
boughtAmount = _tradeUniswapV2(
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (order.source == BridgeSource.UNISWAP) {
|
||||
} else if (protocolId == BridgeProtocols.UNISWAP) {
|
||||
boughtAmount = _tradeUniswap(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (order.source == BridgeSource.BALANCER ||
|
||||
order.source == BridgeSource.CREAM) {
|
||||
} else if (protocolId == BridgeProtocols.BALANCER) {
|
||||
boughtAmount = _tradeBalancer(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (order.source == BridgeSource.KYBER) {
|
||||
} else if (protocolId == BridgeProtocols.KYBER) {
|
||||
boughtAmount = _tradeKyber(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (order.source == BridgeSource.MOONISWAP) {
|
||||
} else if (protocolId == BridgeProtocols.MOONISWAP) {
|
||||
boughtAmount = _tradeMooniswap(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (order.source == BridgeSource.MSTABLE) {
|
||||
} else if (protocolId == BridgeProtocols.MSTABLE) {
|
||||
boughtAmount = _tradeMStable(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (order.source == BridgeSource.OASIS) {
|
||||
} else if (protocolId == BridgeProtocols.OASIS) {
|
||||
boughtAmount = _tradeOasis(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (order.source == BridgeSource.SHELL) {
|
||||
} else if (protocolId == BridgeProtocols.SHELL) {
|
||||
boughtAmount = _tradeShell(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (order.source == BridgeSource.DODO) {
|
||||
} else if (protocolId == BridgeProtocols.DODO) {
|
||||
boughtAmount = _tradeDodo(
|
||||
sellToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (order.source == BridgeSource.DODOV2) {
|
||||
} else if (protocolId == BridgeProtocols.DODOV2) {
|
||||
boughtAmount = _tradeDodoV2(
|
||||
sellToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (order.source == BridgeSource.CRYPTOCOM) {
|
||||
} else if (protocolId == BridgeProtocols.CRYPTOCOM) {
|
||||
boughtAmount = _tradeCryptoCom(
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (order.source == BridgeSource.BANCOR) {
|
||||
} else if (protocolId == BridgeProtocols.BANCOR) {
|
||||
boughtAmount = _tradeBancor(
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (order.source == BridgeSource.COFIX) {
|
||||
} else if (protocolId == BridgeProtocols.COFIX) {
|
||||
boughtAmount = _tradeCoFiX(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.NERVE) {
|
||||
boughtAmount = _tradeNerve(
|
||||
sellToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else {
|
||||
boughtAmount = _tradeZeroExBridge(
|
||||
sellToken,
|
||||
|
@@ -0,0 +1,46 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2021 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||
|
||||
|
||||
library BridgeProtocols {
|
||||
// A incrementally increasing, append-only list of protocol IDs.
|
||||
// We don't use an enum so solidity doesn't throw when we pass in a
|
||||
// new protocol ID that hasn't been rolled up yet.
|
||||
uint128 internal constant UNKNOWN = 0;
|
||||
uint128 internal constant CURVE = 1;
|
||||
uint128 internal constant UNISWAPV2 = 2;
|
||||
uint128 internal constant UNISWAP = 3;
|
||||
uint128 internal constant BALANCER = 4;
|
||||
uint128 internal constant KYBER = 5;
|
||||
uint128 internal constant MOONISWAP = 6;
|
||||
uint128 internal constant MSTABLE = 7;
|
||||
uint128 internal constant OASIS = 8;
|
||||
uint128 internal constant SHELL = 9;
|
||||
uint128 internal constant DODO = 10;
|
||||
uint128 internal constant DODOV2 = 11;
|
||||
uint128 internal constant CRYPTOCOM = 12;
|
||||
uint128 internal constant BANCOR = 13;
|
||||
uint128 internal constant COFIX = 14;
|
||||
uint128 internal constant NERVE = 15;
|
||||
}
|
@@ -1,47 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2020 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
|
||||
library BridgeSource {
|
||||
uint256 constant internal BALANCER = 0;
|
||||
uint256 constant internal BANCOR = 1;
|
||||
uint256 constant internal COFIX = 2;
|
||||
uint256 constant internal CURVE = 3;
|
||||
uint256 constant internal CREAM = 4;
|
||||
uint256 constant internal CRYPTOCOM = 5;
|
||||
uint256 constant internal DODO = 6;
|
||||
uint256 constant internal KYBER = 7;
|
||||
uint256 constant internal LIQUIDITYPROVIDER = 8;
|
||||
uint256 constant internal MOONISWAP = 9;
|
||||
uint256 constant internal MSTABLE = 10;
|
||||
uint256 constant internal OASIS = 11;
|
||||
uint256 constant internal SHELL = 12;
|
||||
uint256 constant internal SNOWSWAP = 13;
|
||||
uint256 constant internal SUSHISWAP = 14;
|
||||
uint256 constant internal SWERVE = 15;
|
||||
uint256 constant internal UNISWAP = 16;
|
||||
uint256 constant internal UNISWAPV2 = 17;
|
||||
uint256 constant internal DODOV2 = 18;
|
||||
uint256 constant internal LINKSWAP = 19;
|
||||
// New sources should be APPENDED to this list, taking the next highest
|
||||
// integer value.
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2020 ZeroEx Intl.
|
||||
Copyright 2021 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -26,20 +26,24 @@ import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||
interface IBridgeAdapter {
|
||||
|
||||
struct BridgeOrder {
|
||||
uint256 source;
|
||||
// Upper 16 bytes: uint128 protocol ID (right-aligned)
|
||||
// Lower 16 bytes: ASCII source name (left-aligned)
|
||||
bytes32 source;
|
||||
uint256 takerTokenAmount;
|
||||
uint256 makerTokenAmount;
|
||||
bytes bridgeData;
|
||||
}
|
||||
|
||||
/// @dev Emitted when tokens are swapped with an external source.
|
||||
/// @param source The unique ID for the source. See `BridgeSource.sol`
|
||||
/// @param source A unique ID for the source, where the upper 16 bytes
|
||||
/// encodes the (right-aligned) uint128 protocol ID and the
|
||||
/// lower 16 bytes encodes an ASCII source name.
|
||||
/// @param inputToken The token the bridge is converting from.
|
||||
/// @param outputToken The token the bridge is converting to.
|
||||
/// @param inputTokenAmount Amount of input token sold.
|
||||
/// @param outputTokenAmount Amount of output token bought.
|
||||
event BridgeFill(
|
||||
uint256 source,
|
||||
bytes32 source,
|
||||
IERC20TokenV06 inputToken,
|
||||
IERC20TokenV06 outputToken,
|
||||
uint256 inputTokenAmount,
|
||||
|
@@ -0,0 +1,72 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2020 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
|
||||
|
||||
contract MixinNerve {
|
||||
|
||||
using LibERC20TokenV06 for IERC20TokenV06;
|
||||
using LibSafeMathV06 for uint256;
|
||||
using LibRichErrorsV06 for bytes;
|
||||
|
||||
|
||||
struct NerveBridgeData {
|
||||
address pool;
|
||||
bytes4 exchangeFunctionSelector;
|
||||
int128 fromCoinIdx;
|
||||
int128 toCoinIdx;
|
||||
}
|
||||
|
||||
function _tradeNerve(
|
||||
IERC20TokenV06 sellToken,
|
||||
uint256 sellAmount,
|
||||
bytes memory bridgeData
|
||||
)
|
||||
internal
|
||||
returns (uint256 boughtAmount)
|
||||
{
|
||||
// Basically a Curve fork but the swap option has a deadline
|
||||
|
||||
// Decode the bridge data to get the Curve metadata.
|
||||
NerveBridgeData memory data = abi.decode(bridgeData, (NerveBridgeData));
|
||||
sellToken.approveIfBelow(data.pool, sellAmount);
|
||||
(bool success, bytes memory resultData) =
|
||||
data.pool.call(abi.encodeWithSelector(
|
||||
data.exchangeFunctionSelector,
|
||||
data.fromCoinIdx,
|
||||
data.toCoinIdx,
|
||||
// dx
|
||||
sellAmount,
|
||||
// min dy
|
||||
1,
|
||||
// deadline
|
||||
block.timestamp
|
||||
));
|
||||
if (!success) {
|
||||
resultData.rrevert();
|
||||
}
|
||||
return abi.decode(resultData, (uint256));
|
||||
}
|
||||
}
|
@@ -1,75 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/*
|
||||
|
||||
Copyright 2020 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||
import "./MixinUniswapV2.sol";
|
||||
|
||||
contract MixinSushiswap {
|
||||
|
||||
using LibERC20TokenV06 for IERC20TokenV06;
|
||||
|
||||
function _tradeSushiswap(
|
||||
IERC20TokenV06 buyToken,
|
||||
uint256 sellAmount,
|
||||
bytes memory bridgeData
|
||||
)
|
||||
internal
|
||||
returns (uint256 boughtAmount)
|
||||
{
|
||||
IERC20TokenV06[] memory path;
|
||||
IUniswapV2Router02 router;
|
||||
{
|
||||
address[] memory _path;
|
||||
(router, _path) =
|
||||
abi.decode(bridgeData, (IUniswapV2Router02, address[]));
|
||||
// To get around `abi.decode()` not supporting interface array types.
|
||||
assembly { path := _path }
|
||||
}
|
||||
|
||||
require(path.length >= 2, "MixinSushiswap/PATH_LENGTH_MUST_BE_AT_LEAST_TWO");
|
||||
require(
|
||||
path[path.length - 1] == buyToken,
|
||||
"MixinSushiswap/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN"
|
||||
);
|
||||
// Grant the Uniswap router an allowance to sell the first token.
|
||||
path[0].approveIfBelow(
|
||||
address(router),
|
||||
sellAmount
|
||||
);
|
||||
|
||||
uint[] memory amounts = router.swapExactTokensForTokens(
|
||||
// Sell all tokens we hold.
|
||||
sellAmount,
|
||||
// Minimum buy amount.
|
||||
1,
|
||||
// Convert to `buyToken` along this path.
|
||||
path,
|
||||
// Recipient is `this`.
|
||||
address(this),
|
||||
// Expires after this block.
|
||||
block.timestamp
|
||||
);
|
||||
return amounts[amounts.length-1];
|
||||
}
|
||||
}
|
@@ -26,12 +26,7 @@ import "../src/fixins/FixinTokenSpender.sol";
|
||||
contract TestFixinTokenSpender is
|
||||
FixinTokenSpender
|
||||
{
|
||||
uint256 constant private TRIGGER_FALLBACK_SUCCESS_AMOUNT = 1340;
|
||||
|
||||
constructor(bytes32 greedyTokensBloomFilter)
|
||||
public
|
||||
FixinTokenSpender(greedyTokensBloomFilter)
|
||||
{}
|
||||
constructor() public {}
|
||||
|
||||
function transferERC20Tokens(
|
||||
IERC20TokenV06 token,
|
||||
@@ -56,21 +51,6 @@ contract TestFixinTokenSpender is
|
||||
uint256 amount
|
||||
);
|
||||
|
||||
// This is called as a fallback when the original transferFrom() fails.
|
||||
function _spendERC20Tokens(
|
||||
IERC20TokenV06 token,
|
||||
address owner,
|
||||
address to,
|
||||
uint256 amount
|
||||
)
|
||||
external
|
||||
{
|
||||
require(amount == TRIGGER_FALLBACK_SUCCESS_AMOUNT,
|
||||
"TokenSpenderFallback/FAILURE_AMOUNT");
|
||||
|
||||
emit FallbackCalled(address(token), owner, to, amount);
|
||||
}
|
||||
|
||||
function getSpendableERC20BalanceOf(
|
||||
IERC20TokenV06 token,
|
||||
address owner
|
||||
@@ -81,12 +61,4 @@ contract TestFixinTokenSpender is
|
||||
{
|
||||
return _getSpendableERC20BalanceOf(token, owner);
|
||||
}
|
||||
|
||||
function isTokenPossiblyGreedy(IERC20TokenV06 token)
|
||||
external
|
||||
view
|
||||
returns (bool)
|
||||
{
|
||||
return _isTokenPossiblyGreedy(token);
|
||||
}
|
||||
}
|
||||
|
@@ -35,8 +35,7 @@ contract TestMetaTransactionsNativeOrdersFeature is
|
||||
IEtherTokenV06(0),
|
||||
IStaking(0),
|
||||
FeeCollectorController(address(new TestFeeCollectorController())),
|
||||
0,
|
||||
bytes32(0)
|
||||
0
|
||||
)
|
||||
{}
|
||||
|
||||
|
@@ -38,7 +38,7 @@ contract TestMetaTransactionsTransformERC20Feature is
|
||||
Transformation[] transformations
|
||||
);
|
||||
|
||||
constructor() public TransformERC20Feature(0) {}
|
||||
constructor() public TransformERC20Feature() {}
|
||||
|
||||
function _transformERC20(TransformERC20Args memory args)
|
||||
public
|
||||
|
@@ -13,8 +13,7 @@ contract TestNativeOrdersFeature is
|
||||
IEtherTokenV06 weth,
|
||||
IStaking staking,
|
||||
FeeCollectorController _feeCollectorController, // Unused but necessary for artifact compatibility.
|
||||
uint32 protocolFeeMultiplier,
|
||||
bytes32 greedyTokensBloomFilter
|
||||
uint32 protocolFeeMultiplier
|
||||
)
|
||||
public
|
||||
NativeOrdersFeature(
|
||||
@@ -22,8 +21,7 @@ contract TestNativeOrdersFeature is
|
||||
weth,
|
||||
staking,
|
||||
FeeCollectorController(address(new TestFeeCollectorController())),
|
||||
protocolFeeMultiplier,
|
||||
greedyTokensBloomFilter
|
||||
protocolFeeMultiplier
|
||||
)
|
||||
{
|
||||
// solhint-disable no-empty-blocks
|
||||
|
@@ -1,31 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2020 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "../src/features/TokenSpenderFeature.sol";
|
||||
|
||||
contract TestTokenSpender is
|
||||
TokenSpenderFeature
|
||||
{
|
||||
modifier onlySelf() override {
|
||||
_;
|
||||
}
|
||||
}
|
@@ -30,5 +30,5 @@ contract TestTransformERC20 is
|
||||
_;
|
||||
}
|
||||
|
||||
constructor() public TransformERC20Feature(0) {}
|
||||
constructor() public TransformERC20Feature() {}
|
||||
}
|
||||
|
@@ -41,9 +41,9 @@
|
||||
"rollback": "node ./lib/scripts/rollback.js"
|
||||
},
|
||||
"config": {
|
||||
"publicInterfaceContracts": "IZeroEx,ZeroEx,FullMigration,InitialMigration,IFlashWallet,IAllowanceTarget,IERC20Transformer,IOwnableFeature,ISimpleFunctionRegistryFeature,ITokenSpenderFeature,ITransformERC20Feature,FillQuoteTransformer,PayTakerTransformer,PositiveSlippageFeeTransformer,WethTransformer,OwnableFeature,SimpleFunctionRegistryFeature,TransformERC20Feature,TokenSpenderFeature,AffiliateFeeTransformer,MetaTransactionsFeature,LogMetadataTransformer,BridgeAdapter,LiquidityProviderFeature,ILiquidityProviderFeature,NativeOrdersFeature,INativeOrdersFeature,FeeCollectorController,FeeCollector,CurveLiquidityProvider,BatchFillNativeOrdersFeature,IBatchFillNativeOrdersFeature,MultiplexFeature,IMultiplexFeature",
|
||||
"publicInterfaceContracts": "IZeroEx,ZeroEx,FullMigration,InitialMigration,IFlashWallet,IERC20Transformer,IOwnableFeature,ISimpleFunctionRegistryFeature,ITransformERC20Feature,FillQuoteTransformer,PayTakerTransformer,PositiveSlippageFeeTransformer,WethTransformer,OwnableFeature,SimpleFunctionRegistryFeature,TransformERC20Feature,AffiliateFeeTransformer,MetaTransactionsFeature,LogMetadataTransformer,BridgeAdapter,LiquidityProviderFeature,ILiquidityProviderFeature,NativeOrdersFeature,INativeOrdersFeature,FeeCollectorController,FeeCollector,CurveLiquidityProvider,BatchFillNativeOrdersFeature,IBatchFillNativeOrdersFeature,MultiplexFeature,IMultiplexFeature",
|
||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
|
||||
"abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|AllowanceTarget|BatchFillNativeOrdersFeature|BootstrapFeature|BridgeAdapter|BridgeSource|CurveLiquidityProvider|FeeCollector|FeeCollectorController|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinProtocolFees|FixinReentrancyGuard|FixinTokenSpender|FlashWallet|FullMigration|IAllowanceTarget|IBatchFillNativeOrdersFeature|IBootstrapFeature|IBridgeAdapter|IERC20Bridge|IERC20Transformer|IFeature|IFlashWallet|ILiquidityProvider|ILiquidityProviderFeature|ILiquidityProviderSandbox|IMetaTransactionsFeature|IMooniswapPool|IMultiplexFeature|INativeOrdersEvents|INativeOrdersFeature|IOwnableFeature|ISimpleFunctionRegistryFeature|IStaking|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IUniswapV2Pair|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC20Transformer|LibFeeCollector|LibLiquidityProviderRichErrors|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibNativeOrder|LibNativeOrdersRichErrors|LibNativeOrdersStorage|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignature|LibSignatureRichErrors|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibSpenderRichErrors|LibStorage|LibTokenSpenderStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LiquidityProviderFeature|LiquidityProviderSandbox|LogMetadataTransformer|MetaTransactionsFeature|MixinBalancer|MixinBancor|MixinCoFiX|MixinCryptoCom|MixinCurve|MixinDodo|MixinDodoV2|MixinKyber|MixinMStable|MixinMooniswap|MixinOasis|MixinShell|MixinSushiswap|MixinUniswap|MixinUniswapV2|MixinZeroExBridge|MooniswapLiquidityProvider|MultiplexFeature|NativeOrdersCancellation|NativeOrdersFeature|NativeOrdersInfo|NativeOrdersProtocolFees|NativeOrdersSettlement|OwnableFeature|PayTakerTransformer|PermissionlessTransformerDeployer|PositiveSlippageFeeTransformer|SimpleFunctionRegistryFeature|TestBridge|TestCallTarget|TestCurve|TestDelegateCaller|TestFeeCollectorController|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFixinProtocolFees|TestFixinTokenSpender|TestFullMigration|TestInitialMigration|TestLibNativeOrder|TestLibSignature|TestLiquidityProvider|TestMetaTransactionsNativeOrdersFeature|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC20Token|TestMooniswap|TestNativeOrdersFeature|TestPermissionlessTransformerDeployerSuicidal|TestPermissionlessTransformerDeployerTransformer|TestRfqOriginRegistration|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestStaking|TestTokenSpender|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestWeth|TestWethTransformerHost|TestZeroExFeature|TokenSpenderFeature|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|WethTransformer|ZeroEx|ZeroExOptimized).json"
|
||||
"abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|BatchFillNativeOrdersFeature|BootstrapFeature|BridgeAdapter|BridgeProtocols|CurveLiquidityProvider|FeeCollector|FeeCollectorController|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinProtocolFees|FixinReentrancyGuard|FixinTokenSpender|FlashWallet|FullMigration|IBatchFillNativeOrdersFeature|IBootstrapFeature|IBridgeAdapter|IERC20Bridge|IERC20Transformer|IFeature|IFlashWallet|ILiquidityProvider|ILiquidityProviderFeature|ILiquidityProviderSandbox|IMetaTransactionsFeature|IMooniswapPool|IMultiplexFeature|INativeOrdersEvents|INativeOrdersFeature|IOwnableFeature|IPancakeSwapFeature|ISimpleFunctionRegistryFeature|IStaking|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IUniswapV2Pair|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC20Transformer|LibFeeCollector|LibLiquidityProviderRichErrors|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibNativeOrder|LibNativeOrdersRichErrors|LibNativeOrdersStorage|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignature|LibSignatureRichErrors|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LiquidityProviderFeature|LiquidityProviderSandbox|LogMetadataTransformer|MetaTransactionsFeature|MixinBalancer|MixinBancor|MixinCoFiX|MixinCryptoCom|MixinCurve|MixinDodo|MixinDodoV2|MixinKyber|MixinMStable|MixinMooniswap|MixinNerve|MixinOasis|MixinShell|MixinUniswap|MixinUniswapV2|MixinZeroExBridge|MooniswapLiquidityProvider|MultiplexFeature|NativeOrdersCancellation|NativeOrdersFeature|NativeOrdersInfo|NativeOrdersProtocolFees|NativeOrdersSettlement|OwnableFeature|PancakeSwapFeature|PayTakerTransformer|PermissionlessTransformerDeployer|PositiveSlippageFeeTransformer|SimpleFunctionRegistryFeature|TestBridge|TestCallTarget|TestCurve|TestDelegateCaller|TestFeeCollectorController|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFixinProtocolFees|TestFixinTokenSpender|TestFullMigration|TestInitialMigration|TestLibNativeOrder|TestLibSignature|TestLiquidityProvider|TestMetaTransactionsNativeOrdersFeature|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC20Token|TestMooniswap|TestNativeOrdersFeature|TestPermissionlessTransformerDeployerSuicidal|TestPermissionlessTransformerDeployerTransformer|TestRfqOriginRegistration|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestStaking|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestWeth|TestWethTransformerHost|TestZeroExFeature|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|WethTransformer|ZeroEx|ZeroExOptimized).json"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@@ -23,7 +23,6 @@ import * as INativeOrdersFeature from '../generated-artifacts/INativeOrdersFeatu
|
||||
import * as InitialMigration from '../generated-artifacts/InitialMigration.json';
|
||||
import * as IOwnableFeature from '../generated-artifacts/IOwnableFeature.json';
|
||||
import * as ISimpleFunctionRegistryFeature from '../generated-artifacts/ISimpleFunctionRegistryFeature.json';
|
||||
import * as ITokenSpenderFeature from '../generated-artifacts/ITokenSpenderFeature.json';
|
||||
import * as ITransformERC20Feature from '../generated-artifacts/ITransformERC20Feature.json';
|
||||
import * as IZeroEx from '../generated-artifacts/IZeroEx.json';
|
||||
import * as LiquidityProviderFeature from '../generated-artifacts/LiquidityProviderFeature.json';
|
||||
@@ -35,7 +34,6 @@ import * as OwnableFeature from '../generated-artifacts/OwnableFeature.json';
|
||||
import * as PayTakerTransformer from '../generated-artifacts/PayTakerTransformer.json';
|
||||
import * as PositiveSlippageFeeTransformer from '../generated-artifacts/PositiveSlippageFeeTransformer.json';
|
||||
import * as SimpleFunctionRegistryFeature from '../generated-artifacts/SimpleFunctionRegistryFeature.json';
|
||||
import * as TokenSpenderFeature from '../generated-artifacts/TokenSpenderFeature.json';
|
||||
import * as TransformERC20Feature from '../generated-artifacts/TransformERC20Feature.json';
|
||||
import * as WethTransformer from '../generated-artifacts/WethTransformer.json';
|
||||
import * as ZeroEx from '../generated-artifacts/ZeroEx.json';
|
||||
@@ -49,7 +47,6 @@ export const artifacts = {
|
||||
IERC20Transformer: IERC20Transformer as ContractArtifact,
|
||||
IOwnableFeature: IOwnableFeature as ContractArtifact,
|
||||
ISimpleFunctionRegistryFeature: ISimpleFunctionRegistryFeature as ContractArtifact,
|
||||
ITokenSpenderFeature: ITokenSpenderFeature as ContractArtifact,
|
||||
ITransformERC20Feature: ITransformERC20Feature as ContractArtifact,
|
||||
FillQuoteTransformer: FillQuoteTransformer as ContractArtifact,
|
||||
PayTakerTransformer: PayTakerTransformer as ContractArtifact,
|
||||
@@ -58,7 +55,6 @@ export const artifacts = {
|
||||
OwnableFeature: OwnableFeature as ContractArtifact,
|
||||
SimpleFunctionRegistryFeature: SimpleFunctionRegistryFeature as ContractArtifact,
|
||||
TransformERC20Feature: TransformERC20Feature as ContractArtifact,
|
||||
TokenSpenderFeature: TokenSpenderFeature as ContractArtifact,
|
||||
AffiliateFeeTransformer: AffiliateFeeTransformer as ContractArtifact,
|
||||
MetaTransactionsFeature: MetaTransactionsFeature as ContractArtifact,
|
||||
LogMetadataTransformer: LogMetadataTransformer as ContractArtifact,
|
||||
|
@@ -41,7 +41,6 @@ export {
|
||||
IOwnableFeatureEvents,
|
||||
ISimpleFunctionRegistryFeatureContract,
|
||||
ISimpleFunctionRegistryFeatureEvents,
|
||||
ITokenSpenderFeatureContract,
|
||||
ITransformERC20FeatureContract,
|
||||
IZeroExContract,
|
||||
LogMetadataTransformerContract,
|
||||
|
@@ -5,7 +5,6 @@ import { TxData } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { artifacts } from './artifacts';
|
||||
import { ZERO_BYTES32 } from './constants';
|
||||
import {
|
||||
FeeCollectorControllerContract,
|
||||
FullMigrationContract,
|
||||
@@ -15,7 +14,6 @@ import {
|
||||
NativeOrdersFeatureContract,
|
||||
OwnableFeatureContract,
|
||||
SimpleFunctionRegistryFeatureContract,
|
||||
TokenSpenderFeatureContract,
|
||||
TransformERC20FeatureContract,
|
||||
ZeroExContract,
|
||||
} from './wrappers';
|
||||
@@ -108,7 +106,6 @@ export async function initialMigrateAsync(
|
||||
* Addresses of features for a full deployment of the Exchange Proxy.
|
||||
*/
|
||||
export interface FullFeatures extends BootstrapFeatures {
|
||||
tokenSpender: string;
|
||||
transformERC20: string;
|
||||
metaTransactions: string;
|
||||
nativeOrders: string;
|
||||
@@ -118,7 +115,6 @@ export interface FullFeatures extends BootstrapFeatures {
|
||||
* Artifacts to use when deploying full features.
|
||||
*/
|
||||
export interface FullFeatureArtifacts extends BootstrapFeatureArtifacts {
|
||||
tokenSpender: SimpleContractArtifact;
|
||||
transformERC20: SimpleContractArtifact;
|
||||
metaTransactions: SimpleContractArtifact;
|
||||
nativeOrders: SimpleContractArtifact;
|
||||
@@ -133,7 +129,6 @@ export interface FullFeaturesDeployConfig {
|
||||
wethAddress: string;
|
||||
stakingAddress: string;
|
||||
protocolFeeMultiplier: number;
|
||||
greedyTokensBloomFilter: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -149,11 +144,9 @@ const DEFAULT_FULL_FEATURES_DEPLOY_CONFIG = {
|
||||
stakingAddress: NULL_ADDRESS,
|
||||
feeCollectorController: NULL_ADDRESS,
|
||||
protocolFeeMultiplier: 70e3,
|
||||
greedyTokensBloomFilter: ZERO_BYTES32,
|
||||
};
|
||||
|
||||
const DEFAULT_FULL_FEATURES_ARTIFACTS = {
|
||||
tokenSpender: artifacts.TokenSpenderFeature,
|
||||
transformERC20: artifacts.TransformERC20Feature,
|
||||
metaTransactions: artifacts.MetaTransactionsFeature,
|
||||
nativeOrders: artifacts.NativeOrdersFeature,
|
||||
@@ -187,14 +180,6 @@ export async function deployFullFeaturesAsync(
|
||||
}
|
||||
return {
|
||||
...(await deployBootstrapFeaturesAsync(provider, txDefaults)),
|
||||
tokenSpender:
|
||||
features.tokenSpender ||
|
||||
(await TokenSpenderFeatureContract.deployFrom0xArtifactAsync(
|
||||
_featureArtifacts.tokenSpender,
|
||||
provider,
|
||||
txDefaults,
|
||||
artifacts,
|
||||
)).address,
|
||||
transformERC20:
|
||||
features.transformERC20 ||
|
||||
(await TransformERC20FeatureContract.deployFrom0xArtifactAsync(
|
||||
@@ -202,7 +187,6 @@ export async function deployFullFeaturesAsync(
|
||||
provider,
|
||||
txDefaults,
|
||||
artifacts,
|
||||
_config.greedyTokensBloomFilter,
|
||||
)).address,
|
||||
metaTransactions:
|
||||
features.metaTransactions ||
|
||||
@@ -212,7 +196,6 @@ export async function deployFullFeaturesAsync(
|
||||
txDefaults,
|
||||
artifacts,
|
||||
_config.zeroExAddress,
|
||||
_config.greedyTokensBloomFilter,
|
||||
)).address,
|
||||
nativeOrders:
|
||||
features.nativeOrders ||
|
||||
@@ -226,7 +209,6 @@ export async function deployFullFeaturesAsync(
|
||||
_config.stakingAddress,
|
||||
_config.feeCollectorController,
|
||||
_config.protocolFeeMultiplier,
|
||||
_config.greedyTokensBloomFilter,
|
||||
)).address,
|
||||
};
|
||||
}
|
||||
|
@@ -20,7 +20,6 @@ export * from '../generated-wrappers/i_multiplex_feature';
|
||||
export * from '../generated-wrappers/i_native_orders_feature';
|
||||
export * from '../generated-wrappers/i_ownable_feature';
|
||||
export * from '../generated-wrappers/i_simple_function_registry_feature';
|
||||
export * from '../generated-wrappers/i_token_spender_feature';
|
||||
export * from '../generated-wrappers/i_transform_erc20_feature';
|
||||
export * from '../generated-wrappers/i_zero_ex';
|
||||
export * from '../generated-wrappers/initial_migration';
|
||||
@@ -33,7 +32,6 @@ export * from '../generated-wrappers/ownable_feature';
|
||||
export * from '../generated-wrappers/pay_taker_transformer';
|
||||
export * from '../generated-wrappers/positive_slippage_fee_transformer';
|
||||
export * from '../generated-wrappers/simple_function_registry_feature';
|
||||
export * from '../generated-wrappers/token_spender_feature';
|
||||
export * from '../generated-wrappers/transform_erc20_feature';
|
||||
export * from '../generated-wrappers/weth_transformer';
|
||||
export * from '../generated-wrappers/zero_ex';
|
||||
|
@@ -1,82 +0,0 @@
|
||||
import { blockchainTests, constants, expect, randomAddress, verifyEventsFromLogs } from '@0x/contracts-test-utils';
|
||||
import { AuthorizableRevertErrors, hexUtils, StringRevertError } from '@0x/utils';
|
||||
|
||||
import { artifacts } from './artifacts';
|
||||
import { AllowanceTargetContract, TestCallTargetContract, TestCallTargetEvents } from './wrappers';
|
||||
|
||||
blockchainTests.resets('AllowanceTarget', env => {
|
||||
let owner: string;
|
||||
let authority: string;
|
||||
let allowanceTarget: AllowanceTargetContract;
|
||||
let callTarget: TestCallTargetContract;
|
||||
|
||||
before(async () => {
|
||||
[owner, authority] = await env.getAccountAddressesAsync();
|
||||
allowanceTarget = await AllowanceTargetContract.deployFrom0xArtifactAsync(
|
||||
artifacts.AllowanceTarget,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
await allowanceTarget.addAuthorizedAddress(authority).awaitTransactionSuccessAsync();
|
||||
callTarget = await TestCallTargetContract.deployFrom0xArtifactAsync(
|
||||
artifacts.TestCallTarget,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
});
|
||||
|
||||
const TARGET_RETURN_VALUE = hexUtils.rightPad('0x12345678');
|
||||
const REVERTING_DATA = '0x1337';
|
||||
|
||||
describe('executeCall()', () => {
|
||||
it('non-authority cannot call executeCall()', async () => {
|
||||
const notAuthority = randomAddress();
|
||||
const tx = allowanceTarget
|
||||
.executeCall(randomAddress(), hexUtils.random())
|
||||
.callAsync({ from: notAuthority });
|
||||
return expect(tx).to.revertWith(new AuthorizableRevertErrors.SenderNotAuthorizedError(notAuthority));
|
||||
});
|
||||
|
||||
it('authority can call executeCall()', async () => {
|
||||
const targetData = hexUtils.random(128);
|
||||
const receipt = await allowanceTarget
|
||||
.executeCall(callTarget.address, targetData)
|
||||
.awaitTransactionSuccessAsync({ from: authority });
|
||||
verifyEventsFromLogs(
|
||||
receipt.logs,
|
||||
[
|
||||
{
|
||||
context: callTarget.address,
|
||||
sender: allowanceTarget.address,
|
||||
data: targetData,
|
||||
value: constants.ZERO_AMOUNT,
|
||||
},
|
||||
],
|
||||
TestCallTargetEvents.CallTargetCalled,
|
||||
);
|
||||
});
|
||||
|
||||
it('AllowanceTarget returns call result', async () => {
|
||||
const result = await allowanceTarget
|
||||
.executeCall(callTarget.address, hexUtils.random(128))
|
||||
.callAsync({ from: authority });
|
||||
expect(result).to.eq(TARGET_RETURN_VALUE);
|
||||
});
|
||||
|
||||
it('AllowanceTarget returns raw call revert', async () => {
|
||||
const tx = allowanceTarget.executeCall(callTarget.address, REVERTING_DATA).callAsync({ from: authority });
|
||||
return expect(tx).to.revertWith(new StringRevertError('TestCallTarget/REVERT'));
|
||||
});
|
||||
|
||||
it('AllowanceTarget cannot receive ETH', async () => {
|
||||
const tx = env.web3Wrapper.sendTransactionAsync({
|
||||
to: allowanceTarget.address,
|
||||
from: owner,
|
||||
value: 0,
|
||||
});
|
||||
return expect(tx).to.eventually.be.rejected();
|
||||
});
|
||||
});
|
||||
});
|
@@ -6,11 +6,10 @@
|
||||
import { ContractArtifact } from 'ethereum-types';
|
||||
|
||||
import * as AffiliateFeeTransformer from '../test/generated-artifacts/AffiliateFeeTransformer.json';
|
||||
import * as AllowanceTarget from '../test/generated-artifacts/AllowanceTarget.json';
|
||||
import * as BatchFillNativeOrdersFeature from '../test/generated-artifacts/BatchFillNativeOrdersFeature.json';
|
||||
import * as BootstrapFeature from '../test/generated-artifacts/BootstrapFeature.json';
|
||||
import * as BridgeAdapter from '../test/generated-artifacts/BridgeAdapter.json';
|
||||
import * as BridgeSource from '../test/generated-artifacts/BridgeSource.json';
|
||||
import * as BridgeProtocols from '../test/generated-artifacts/BridgeProtocols.json';
|
||||
import * as CurveLiquidityProvider from '../test/generated-artifacts/CurveLiquidityProvider.json';
|
||||
import * as FeeCollector from '../test/generated-artifacts/FeeCollector.json';
|
||||
import * as FeeCollectorController from '../test/generated-artifacts/FeeCollectorController.json';
|
||||
@@ -22,7 +21,6 @@ import * as FixinReentrancyGuard from '../test/generated-artifacts/FixinReentran
|
||||
import * as FixinTokenSpender from '../test/generated-artifacts/FixinTokenSpender.json';
|
||||
import * as FlashWallet from '../test/generated-artifacts/FlashWallet.json';
|
||||
import * as FullMigration from '../test/generated-artifacts/FullMigration.json';
|
||||
import * as IAllowanceTarget from '../test/generated-artifacts/IAllowanceTarget.json';
|
||||
import * as IBatchFillNativeOrdersFeature from '../test/generated-artifacts/IBatchFillNativeOrdersFeature.json';
|
||||
import * as IBootstrapFeature from '../test/generated-artifacts/IBootstrapFeature.json';
|
||||
import * as IBridgeAdapter from '../test/generated-artifacts/IBridgeAdapter.json';
|
||||
@@ -40,6 +38,7 @@ import * as INativeOrdersEvents from '../test/generated-artifacts/INativeOrdersE
|
||||
import * as INativeOrdersFeature from '../test/generated-artifacts/INativeOrdersFeature.json';
|
||||
import * as InitialMigration from '../test/generated-artifacts/InitialMigration.json';
|
||||
import * as IOwnableFeature from '../test/generated-artifacts/IOwnableFeature.json';
|
||||
import * as IPancakeSwapFeature from '../test/generated-artifacts/IPancakeSwapFeature.json';
|
||||
import * as ISimpleFunctionRegistryFeature from '../test/generated-artifacts/ISimpleFunctionRegistryFeature.json';
|
||||
import * as IStaking from '../test/generated-artifacts/IStaking.json';
|
||||
import * as ITestSimpleFunctionRegistryFeature from '../test/generated-artifacts/ITestSimpleFunctionRegistryFeature.json';
|
||||
@@ -68,9 +67,7 @@ import * as LibSignature from '../test/generated-artifacts/LibSignature.json';
|
||||
import * as LibSignatureRichErrors from '../test/generated-artifacts/LibSignatureRichErrors.json';
|
||||
import * as LibSimpleFunctionRegistryRichErrors from '../test/generated-artifacts/LibSimpleFunctionRegistryRichErrors.json';
|
||||
import * as LibSimpleFunctionRegistryStorage from '../test/generated-artifacts/LibSimpleFunctionRegistryStorage.json';
|
||||
import * as LibSpenderRichErrors from '../test/generated-artifacts/LibSpenderRichErrors.json';
|
||||
import * as LibStorage from '../test/generated-artifacts/LibStorage.json';
|
||||
import * as LibTokenSpenderStorage from '../test/generated-artifacts/LibTokenSpenderStorage.json';
|
||||
import * as LibTransformERC20RichErrors from '../test/generated-artifacts/LibTransformERC20RichErrors.json';
|
||||
import * as LibTransformERC20Storage from '../test/generated-artifacts/LibTransformERC20Storage.json';
|
||||
import * as LibWalletRichErrors from '../test/generated-artifacts/LibWalletRichErrors.json';
|
||||
@@ -88,9 +85,9 @@ import * as MixinDodoV2 from '../test/generated-artifacts/MixinDodoV2.json';
|
||||
import * as MixinKyber from '../test/generated-artifacts/MixinKyber.json';
|
||||
import * as MixinMooniswap from '../test/generated-artifacts/MixinMooniswap.json';
|
||||
import * as MixinMStable from '../test/generated-artifacts/MixinMStable.json';
|
||||
import * as MixinNerve from '../test/generated-artifacts/MixinNerve.json';
|
||||
import * as MixinOasis from '../test/generated-artifacts/MixinOasis.json';
|
||||
import * as MixinShell from '../test/generated-artifacts/MixinShell.json';
|
||||
import * as MixinSushiswap from '../test/generated-artifacts/MixinSushiswap.json';
|
||||
import * as MixinUniswap from '../test/generated-artifacts/MixinUniswap.json';
|
||||
import * as MixinUniswapV2 from '../test/generated-artifacts/MixinUniswapV2.json';
|
||||
import * as MixinZeroExBridge from '../test/generated-artifacts/MixinZeroExBridge.json';
|
||||
@@ -102,6 +99,7 @@ import * as NativeOrdersInfo from '../test/generated-artifacts/NativeOrdersInfo.
|
||||
import * as NativeOrdersProtocolFees from '../test/generated-artifacts/NativeOrdersProtocolFees.json';
|
||||
import * as NativeOrdersSettlement from '../test/generated-artifacts/NativeOrdersSettlement.json';
|
||||
import * as OwnableFeature from '../test/generated-artifacts/OwnableFeature.json';
|
||||
import * as PancakeSwapFeature from '../test/generated-artifacts/PancakeSwapFeature.json';
|
||||
import * as PayTakerTransformer from '../test/generated-artifacts/PayTakerTransformer.json';
|
||||
import * as PermissionlessTransformerDeployer from '../test/generated-artifacts/PermissionlessTransformerDeployer.json';
|
||||
import * as PositiveSlippageFeeTransformer from '../test/generated-artifacts/PositiveSlippageFeeTransformer.json';
|
||||
@@ -134,7 +132,6 @@ import * as TestRfqOriginRegistration from '../test/generated-artifacts/TestRfqO
|
||||
import * as TestSimpleFunctionRegistryFeatureImpl1 from '../test/generated-artifacts/TestSimpleFunctionRegistryFeatureImpl1.json';
|
||||
import * as TestSimpleFunctionRegistryFeatureImpl2 from '../test/generated-artifacts/TestSimpleFunctionRegistryFeatureImpl2.json';
|
||||
import * as TestStaking from '../test/generated-artifacts/TestStaking.json';
|
||||
import * as TestTokenSpender from '../test/generated-artifacts/TestTokenSpender.json';
|
||||
import * as TestTokenSpenderERC20Token from '../test/generated-artifacts/TestTokenSpenderERC20Token.json';
|
||||
import * as TestTransformerBase from '../test/generated-artifacts/TestTransformerBase.json';
|
||||
import * as TestTransformERC20 from '../test/generated-artifacts/TestTransformERC20.json';
|
||||
@@ -143,7 +140,6 @@ import * as TestTransformerHost from '../test/generated-artifacts/TestTransforme
|
||||
import * as TestWeth from '../test/generated-artifacts/TestWeth.json';
|
||||
import * as TestWethTransformerHost from '../test/generated-artifacts/TestWethTransformerHost.json';
|
||||
import * as TestZeroExFeature from '../test/generated-artifacts/TestZeroExFeature.json';
|
||||
import * as TokenSpenderFeature from '../test/generated-artifacts/TokenSpenderFeature.json';
|
||||
import * as Transformer from '../test/generated-artifacts/Transformer.json';
|
||||
import * as TransformERC20Feature from '../test/generated-artifacts/TransformERC20Feature.json';
|
||||
import * as TransformerDeployer from '../test/generated-artifacts/TransformerDeployer.json';
|
||||
@@ -163,14 +159,11 @@ export const artifacts = {
|
||||
LibProxyRichErrors: LibProxyRichErrors as ContractArtifact,
|
||||
LibSignatureRichErrors: LibSignatureRichErrors as ContractArtifact,
|
||||
LibSimpleFunctionRegistryRichErrors: LibSimpleFunctionRegistryRichErrors as ContractArtifact,
|
||||
LibSpenderRichErrors: LibSpenderRichErrors as ContractArtifact,
|
||||
LibTransformERC20RichErrors: LibTransformERC20RichErrors as ContractArtifact,
|
||||
LibWalletRichErrors: LibWalletRichErrors as ContractArtifact,
|
||||
AllowanceTarget: AllowanceTarget as ContractArtifact,
|
||||
FeeCollector: FeeCollector as ContractArtifact,
|
||||
FeeCollectorController: FeeCollectorController as ContractArtifact,
|
||||
FlashWallet: FlashWallet as ContractArtifact,
|
||||
IAllowanceTarget: IAllowanceTarget as ContractArtifact,
|
||||
IFlashWallet: IFlashWallet as ContractArtifact,
|
||||
ILiquidityProviderSandbox: ILiquidityProviderSandbox as ContractArtifact,
|
||||
LibFeeCollector: LibFeeCollector as ContractArtifact,
|
||||
@@ -184,8 +177,8 @@ export const artifacts = {
|
||||
MultiplexFeature: MultiplexFeature as ContractArtifact,
|
||||
NativeOrdersFeature: NativeOrdersFeature as ContractArtifact,
|
||||
OwnableFeature: OwnableFeature as ContractArtifact,
|
||||
PancakeSwapFeature: PancakeSwapFeature as ContractArtifact,
|
||||
SimpleFunctionRegistryFeature: SimpleFunctionRegistryFeature as ContractArtifact,
|
||||
TokenSpenderFeature: TokenSpenderFeature as ContractArtifact,
|
||||
TransformERC20Feature: TransformERC20Feature as ContractArtifact,
|
||||
UniswapFeature: UniswapFeature as ContractArtifact,
|
||||
IBatchFillNativeOrdersFeature: IBatchFillNativeOrdersFeature as ContractArtifact,
|
||||
@@ -197,6 +190,7 @@ export const artifacts = {
|
||||
INativeOrdersEvents: INativeOrdersEvents as ContractArtifact,
|
||||
INativeOrdersFeature: INativeOrdersFeature as ContractArtifact,
|
||||
IOwnableFeature: IOwnableFeature as ContractArtifact,
|
||||
IPancakeSwapFeature: IPancakeSwapFeature as ContractArtifact,
|
||||
ISimpleFunctionRegistryFeature: ISimpleFunctionRegistryFeature as ContractArtifact,
|
||||
ITokenSpenderFeature: ITokenSpenderFeature as ContractArtifact,
|
||||
ITransformERC20Feature: ITransformERC20Feature as ContractArtifact,
|
||||
@@ -225,7 +219,6 @@ export const artifacts = {
|
||||
LibReentrancyGuardStorage: LibReentrancyGuardStorage as ContractArtifact,
|
||||
LibSimpleFunctionRegistryStorage: LibSimpleFunctionRegistryStorage as ContractArtifact,
|
||||
LibStorage: LibStorage as ContractArtifact,
|
||||
LibTokenSpenderStorage: LibTokenSpenderStorage as ContractArtifact,
|
||||
LibTransformERC20Storage: LibTransformERC20Storage as ContractArtifact,
|
||||
AffiliateFeeTransformer: AffiliateFeeTransformer as ContractArtifact,
|
||||
FillQuoteTransformer: FillQuoteTransformer as ContractArtifact,
|
||||
@@ -237,7 +230,7 @@ export const artifacts = {
|
||||
Transformer: Transformer as ContractArtifact,
|
||||
WethTransformer: WethTransformer as ContractArtifact,
|
||||
BridgeAdapter: BridgeAdapter as ContractArtifact,
|
||||
BridgeSource: BridgeSource as ContractArtifact,
|
||||
BridgeProtocols: BridgeProtocols as ContractArtifact,
|
||||
IBridgeAdapter: IBridgeAdapter as ContractArtifact,
|
||||
MixinBalancer: MixinBalancer as ContractArtifact,
|
||||
MixinBancor: MixinBancor as ContractArtifact,
|
||||
@@ -249,9 +242,9 @@ export const artifacts = {
|
||||
MixinKyber: MixinKyber as ContractArtifact,
|
||||
MixinMStable: MixinMStable as ContractArtifact,
|
||||
MixinMooniswap: MixinMooniswap as ContractArtifact,
|
||||
MixinNerve: MixinNerve as ContractArtifact,
|
||||
MixinOasis: MixinOasis as ContractArtifact,
|
||||
MixinShell: MixinShell as ContractArtifact,
|
||||
MixinSushiswap: MixinSushiswap as ContractArtifact,
|
||||
MixinUniswap: MixinUniswap as ContractArtifact,
|
||||
MixinUniswapV2: MixinUniswapV2 as ContractArtifact,
|
||||
MixinZeroExBridge: MixinZeroExBridge as ContractArtifact,
|
||||
@@ -289,7 +282,6 @@ export const artifacts = {
|
||||
TestSimpleFunctionRegistryFeatureImpl1: TestSimpleFunctionRegistryFeatureImpl1 as ContractArtifact,
|
||||
TestSimpleFunctionRegistryFeatureImpl2: TestSimpleFunctionRegistryFeatureImpl2 as ContractArtifact,
|
||||
TestStaking: TestStaking as ContractArtifact,
|
||||
TestTokenSpender: TestTokenSpender as ContractArtifact,
|
||||
TestTokenSpenderERC20Token: TestTokenSpenderERC20Token as ContractArtifact,
|
||||
TestTransformERC20: TestTransformERC20 as ContractArtifact,
|
||||
TestTransformerBase: TestTransformerBase as ContractArtifact,
|
||||
|
@@ -2,7 +2,6 @@ import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contra
|
||||
import { blockchainTests, constants, expect, verifyEventsFromLogs } from '@0x/contracts-test-utils';
|
||||
import { BigNumber, OwnableRevertErrors, ZeroExRevertErrors } from '@0x/utils';
|
||||
|
||||
import { ZERO_BYTES32 } from '../../src/constants';
|
||||
import { IOwnableFeatureContract, IZeroExContract, LiquidityProviderFeatureContract } from '../../src/wrappers';
|
||||
import { artifacts } from '../artifacts';
|
||||
import { abis } from '../utils/abis';
|
||||
@@ -63,7 +62,6 @@ blockchainTests('LiquidityProvider feature', env => {
|
||||
env.txDefaults,
|
||||
artifacts,
|
||||
sandbox.address,
|
||||
ZERO_BYTES32,
|
||||
);
|
||||
await new IOwnableFeatureContract(zeroEx.address, env.provider, env.txDefaults, abis)
|
||||
.migrate(featureImpl.address, featureImpl.migrate().getABIEncodedTransactionData(), owner)
|
||||
|
@@ -8,7 +8,8 @@ import {
|
||||
} from '@0x/contracts-erc20';
|
||||
import { blockchainTests, constants, expect, filterLogsToArguments, toBaseUnitAmount } from '@0x/contracts-test-utils';
|
||||
import {
|
||||
BridgeSource,
|
||||
BridgeProtocol,
|
||||
encodeBridgeSourceId,
|
||||
encodeFillQuoteTransformerData,
|
||||
encodePayTakerTransformerData,
|
||||
FillQuoteTransformerOrderType,
|
||||
@@ -72,8 +73,9 @@ blockchainTests.fork.skip('Multiplex feature', env => {
|
||||
const WETH_DAI_PLP_ADDRESS = '0x1db681925786441ba82adefac7bf492089665ca0';
|
||||
const WETH_USDC_PLP_ADDRESS = '0x8463c03c0c57ff19fa8b431e0d3a34e2df89888e';
|
||||
const USDC_USDT_PLP_ADDRESS = '0xc340ef96449514cea4dfa11d847a06d7f03d437c';
|
||||
const GREEDY_TOKENS_BLOOM_FILTER = '0x0000100800000480002c00401000000820000000000000020000001010800001';
|
||||
const BALANCER_WETH_DAI = '0x8b6e6e7b5b3801fed2cafd4b22b8a16c2f2db21a';
|
||||
const CURVE_BRIDGE_SOURCE_ID = encodeBridgeSourceId(BridgeProtocol.Curve, 'Curve');
|
||||
const BALANCER_BRIDGE_SOURCE_ID = encodeBridgeSourceId(BridgeProtocol.Bancor, 'Balancer');
|
||||
const fqtNonce = findTransformerNonce(
|
||||
'0xfa6282736af206cb4cfc5cb786d82aecdf1186f9',
|
||||
'0x39dce47a67ad34344eab877eae3ef1fa2a1d50bb',
|
||||
@@ -112,7 +114,6 @@ blockchainTests.fork.skip('Multiplex feature', env => {
|
||||
zeroEx.address,
|
||||
WETH_ADDRESS,
|
||||
PLP_SANDBOX_ADDRESS,
|
||||
GREEDY_TOKENS_BLOOM_FILTER,
|
||||
);
|
||||
await registry
|
||||
.extend(multiplex.getSelector('batchFill'), multiplexImpl.address)
|
||||
@@ -283,7 +284,7 @@ blockchainTests.fork.skip('Multiplex feature', env => {
|
||||
buyToken: WETH_ADDRESS,
|
||||
bridgeOrders: [
|
||||
{
|
||||
source: BridgeSource.Balancer,
|
||||
source: BALANCER_BRIDGE_SOURCE_ID,
|
||||
takerTokenAmount: expiredRfqCall.sellAmount,
|
||||
makerTokenAmount: expiredRfqCall.sellAmount,
|
||||
bridgeData: poolEncoder.encode([BALANCER_WETH_DAI]),
|
||||
@@ -339,7 +340,7 @@ blockchainTests.fork.skip('Multiplex feature', env => {
|
||||
tx.logs,
|
||||
BridgeAdapterEvents.BridgeFill,
|
||||
);
|
||||
expect(bridgeFillEvent.source).to.bignumber.equal(BridgeSource.Balancer);
|
||||
expect(bridgeFillEvent.source).to.bignumber.equal(BALANCER_BRIDGE_SOURCE_ID);
|
||||
expect(bridgeFillEvent.inputToken).to.equal(DAI_ADDRESS);
|
||||
expect(bridgeFillEvent.outputToken).to.equal(WETH_ADDRESS);
|
||||
expect(bridgeFillEvent.inputTokenAmount).to.bignumber.equal(expiredRfqCall.sellAmount);
|
||||
@@ -472,7 +473,7 @@ blockchainTests.fork.skip('Multiplex feature', env => {
|
||||
buyToken: USDC_ADDRESS,
|
||||
bridgeOrders: [
|
||||
{
|
||||
source: BridgeSource.Curve,
|
||||
source: CURVE_BRIDGE_SOURCE_ID,
|
||||
takerTokenAmount: sellAmount,
|
||||
makerTokenAmount: sellAmount,
|
||||
bridgeData: curveEncoder.encode([
|
||||
@@ -531,7 +532,7 @@ blockchainTests.fork.skip('Multiplex feature', env => {
|
||||
tx.logs,
|
||||
BridgeAdapterEvents.BridgeFill,
|
||||
);
|
||||
expect(bridgeFillEvent.source).to.bignumber.equal(BridgeSource.Curve);
|
||||
expect(bridgeFillEvent.source).to.bignumber.equal(CURVE_BRIDGE_SOURCE_ID);
|
||||
expect(bridgeFillEvent.inputToken).to.equal(DAI_ADDRESS);
|
||||
expect(bridgeFillEvent.outputToken).to.equal(USDC_ADDRESS);
|
||||
expect(bridgeFillEvent.inputTokenAmount).to.bignumber.equal(sellAmount);
|
||||
@@ -563,7 +564,7 @@ blockchainTests.fork.skip('Multiplex feature', env => {
|
||||
buyToken: DAI_ADDRESS,
|
||||
bridgeOrders: [
|
||||
{
|
||||
source: BridgeSource.Curve,
|
||||
source: CURVE_BRIDGE_SOURCE_ID,
|
||||
takerTokenAmount: constants.MAX_UINT256,
|
||||
makerTokenAmount: constants.MAX_UINT256,
|
||||
bridgeData: curveEncoder.encode([
|
||||
@@ -640,7 +641,7 @@ blockchainTests.fork.skip('Multiplex feature', env => {
|
||||
tx.logs,
|
||||
BridgeAdapterEvents.BridgeFill,
|
||||
);
|
||||
expect(bridgeFillEvent.source).to.bignumber.equal(BridgeSource.Curve);
|
||||
expect(bridgeFillEvent.source).to.bignumber.equal(CURVE_BRIDGE_SOURCE_ID);
|
||||
expect(bridgeFillEvent.inputToken).to.equal(USDC_ADDRESS);
|
||||
expect(bridgeFillEvent.outputToken).to.equal(DAI_ADDRESS);
|
||||
expect(bridgeFillEvent.inputTokenAmount).to.bignumber.equal(uniswapOutputAmount);
|
||||
|
@@ -1,137 +0,0 @@
|
||||
import {
|
||||
blockchainTests,
|
||||
expect,
|
||||
getRandomInteger,
|
||||
randomAddress,
|
||||
verifyEventsFromLogs,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { BigNumber, hexUtils, StringRevertError, ZeroExRevertErrors } from '@0x/utils';
|
||||
|
||||
import { IZeroExContract, TokenSpenderFeatureContract } from '../../src/wrappers';
|
||||
import { artifacts } from '../artifacts';
|
||||
import { abis } from '../utils/abis';
|
||||
import { fullMigrateAsync } from '../utils/migration';
|
||||
import { TestTokenSpenderERC20TokenContract, TestTokenSpenderERC20TokenEvents } from '../wrappers';
|
||||
|
||||
blockchainTests.resets('TokenSpender feature', env => {
|
||||
let zeroEx: IZeroExContract;
|
||||
let feature: TokenSpenderFeatureContract;
|
||||
let token: TestTokenSpenderERC20TokenContract;
|
||||
let allowanceTarget: string;
|
||||
|
||||
before(async () => {
|
||||
const [owner] = await env.getAccountAddressesAsync();
|
||||
zeroEx = await fullMigrateAsync(owner, env.provider, env.txDefaults, {
|
||||
tokenSpender: (await TokenSpenderFeatureContract.deployFrom0xArtifactAsync(
|
||||
artifacts.TestTokenSpender,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
artifacts,
|
||||
)).address,
|
||||
});
|
||||
feature = new TokenSpenderFeatureContract(zeroEx.address, env.provider, env.txDefaults, abis);
|
||||
token = await TestTokenSpenderERC20TokenContract.deployFrom0xArtifactAsync(
|
||||
artifacts.TestTokenSpenderERC20Token,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
allowanceTarget = await feature.getAllowanceTarget().callAsync();
|
||||
});
|
||||
|
||||
describe('_spendERC20Tokens()', () => {
|
||||
const EMPTY_RETURN_AMOUNT = 1337;
|
||||
const FALSE_RETURN_AMOUNT = 1338;
|
||||
const REVERT_RETURN_AMOUNT = 1339;
|
||||
|
||||
it('_spendERC20Tokens() successfully calls compliant ERC20 token', async () => {
|
||||
const tokenFrom = randomAddress();
|
||||
const tokenTo = randomAddress();
|
||||
const tokenAmount = new BigNumber(123456);
|
||||
const receipt = await feature
|
||||
._spendERC20Tokens(token.address, tokenFrom, tokenTo, tokenAmount)
|
||||
.awaitTransactionSuccessAsync();
|
||||
verifyEventsFromLogs(
|
||||
receipt.logs,
|
||||
[
|
||||
{
|
||||
sender: allowanceTarget,
|
||||
from: tokenFrom,
|
||||
to: tokenTo,
|
||||
amount: tokenAmount,
|
||||
},
|
||||
],
|
||||
TestTokenSpenderERC20TokenEvents.TransferFromCalled,
|
||||
);
|
||||
});
|
||||
|
||||
it('_spendERC20Tokens() successfully calls non-compliant ERC20 token', async () => {
|
||||
const tokenFrom = randomAddress();
|
||||
const tokenTo = randomAddress();
|
||||
const tokenAmount = new BigNumber(EMPTY_RETURN_AMOUNT);
|
||||
const receipt = await feature
|
||||
._spendERC20Tokens(token.address, tokenFrom, tokenTo, tokenAmount)
|
||||
.awaitTransactionSuccessAsync();
|
||||
verifyEventsFromLogs(
|
||||
receipt.logs,
|
||||
[
|
||||
{
|
||||
sender: allowanceTarget,
|
||||
from: tokenFrom,
|
||||
to: tokenTo,
|
||||
amount: tokenAmount,
|
||||
},
|
||||
],
|
||||
TestTokenSpenderERC20TokenEvents.TransferFromCalled,
|
||||
);
|
||||
});
|
||||
|
||||
it('_spendERC20Tokens() reverts if ERC20 token reverts', async () => {
|
||||
const tokenFrom = randomAddress();
|
||||
const tokenTo = randomAddress();
|
||||
const tokenAmount = new BigNumber(REVERT_RETURN_AMOUNT);
|
||||
const tx = feature
|
||||
._spendERC20Tokens(token.address, tokenFrom, tokenTo, tokenAmount)
|
||||
.awaitTransactionSuccessAsync();
|
||||
const expectedError = new ZeroExRevertErrors.Spender.SpenderERC20TransferFromFailedError(
|
||||
token.address,
|
||||
tokenFrom,
|
||||
tokenTo,
|
||||
tokenAmount,
|
||||
new StringRevertError('TestTokenSpenderERC20Token/Revert').encode(),
|
||||
);
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('_spendERC20Tokens() reverts if ERC20 token returns false', async () => {
|
||||
const tokenFrom = randomAddress();
|
||||
const tokenTo = randomAddress();
|
||||
const tokenAmount = new BigNumber(FALSE_RETURN_AMOUNT);
|
||||
const tx = feature
|
||||
._spendERC20Tokens(token.address, tokenFrom, tokenTo, tokenAmount)
|
||||
.awaitTransactionSuccessAsync();
|
||||
return expect(tx).to.revertWith(
|
||||
new ZeroExRevertErrors.Spender.SpenderERC20TransferFromFailedError(
|
||||
token.address,
|
||||
tokenFrom,
|
||||
tokenTo,
|
||||
tokenAmount,
|
||||
hexUtils.leftPad(0),
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getSpendableERC20BalanceOf()', () => {
|
||||
it("returns the minimum of the owner's balance and allowance", async () => {
|
||||
const balance = getRandomInteger(1, '1e18');
|
||||
const allowance = getRandomInteger(1, '1e18');
|
||||
const tokenOwner = randomAddress();
|
||||
await token
|
||||
.setBalanceAndAllowanceOf(tokenOwner, balance, allowanceTarget, allowance)
|
||||
.awaitTransactionSuccessAsync();
|
||||
const spendableBalance = await feature.getSpendableERC20BalanceOf(token.address, tokenOwner).callAsync();
|
||||
expect(spendableBalance).to.bignumber.eq(BigNumber.min(balance, allowance));
|
||||
});
|
||||
});
|
||||
});
|
@@ -5,14 +5,11 @@ import {
|
||||
randomAddress,
|
||||
verifyEventsFromLogs,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { BigNumber, hexUtils, StringRevertError, ZeroExRevertErrors } from '@0x/utils';
|
||||
|
||||
import { getTokenListBloomFilter } from '../src/bloom_filter_utils';
|
||||
import { BigNumber, hexUtils, RawRevertError, StringRevertError } from '@0x/utils';
|
||||
|
||||
import { artifacts } from './artifacts';
|
||||
import {
|
||||
TestFixinTokenSpenderContract,
|
||||
TestFixinTokenSpenderEvents,
|
||||
TestTokenSpenderERC20TokenContract,
|
||||
TestTokenSpenderERC20TokenEvents,
|
||||
} from './wrappers';
|
||||
@@ -21,7 +18,6 @@ blockchainTests.resets('FixinTokenSpender', env => {
|
||||
let tokenSpender: TestFixinTokenSpenderContract;
|
||||
let token: TestTokenSpenderERC20TokenContract;
|
||||
let greedyToken: TestTokenSpenderERC20TokenContract;
|
||||
let greedyTokensBloomFilter: string;
|
||||
|
||||
before(async () => {
|
||||
token = await TestTokenSpenderERC20TokenContract.deployFrom0xArtifactAsync(
|
||||
@@ -38,14 +34,11 @@ blockchainTests.resets('FixinTokenSpender', env => {
|
||||
);
|
||||
await greedyToken.setGreedyRevert(true).awaitTransactionSuccessAsync();
|
||||
|
||||
greedyTokensBloomFilter = getTokenListBloomFilter([greedyToken.address]);
|
||||
|
||||
tokenSpender = await TestFixinTokenSpenderContract.deployFrom0xArtifactAsync(
|
||||
artifacts.TestFixinTokenSpender,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
artifacts,
|
||||
greedyTokensBloomFilter,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -53,7 +46,6 @@ blockchainTests.resets('FixinTokenSpender', env => {
|
||||
const EMPTY_RETURN_AMOUNT = 1337;
|
||||
const FALSE_RETURN_AMOUNT = 1338;
|
||||
const REVERT_RETURN_AMOUNT = 1339;
|
||||
const TRIGGER_FALLBACK_SUCCESS_AMOUNT = 1340;
|
||||
const EXTRA_RETURN_TRUE_AMOUNT = 1341;
|
||||
const EXTRA_RETURN_FALSE_AMOUNT = 1342;
|
||||
|
||||
@@ -106,13 +98,7 @@ blockchainTests.resets('FixinTokenSpender', env => {
|
||||
const tx = tokenSpender
|
||||
.transferERC20Tokens(token.address, tokenFrom, tokenTo, tokenAmount)
|
||||
.awaitTransactionSuccessAsync();
|
||||
const expectedError = new ZeroExRevertErrors.Spender.SpenderERC20TransferFromFailedError(
|
||||
token.address,
|
||||
tokenFrom,
|
||||
tokenTo,
|
||||
tokenAmount,
|
||||
new StringRevertError('TestTokenSpenderERC20Token/Revert').encode(),
|
||||
);
|
||||
const expectedError = new StringRevertError('TestTokenSpenderERC20Token/Revert');
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
@@ -123,36 +109,7 @@ blockchainTests.resets('FixinTokenSpender', env => {
|
||||
const tx = tokenSpender
|
||||
.transferERC20Tokens(token.address, tokenFrom, tokenTo, tokenAmount)
|
||||
.awaitTransactionSuccessAsync();
|
||||
return expect(tx).to.revertWith(
|
||||
new ZeroExRevertErrors.Spender.SpenderERC20TransferFromFailedError(
|
||||
token.address,
|
||||
tokenFrom,
|
||||
tokenTo,
|
||||
tokenAmount,
|
||||
hexUtils.leftPad(0),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
it('transferERC20Tokens() falls back successfully to TokenSpender._spendERC20Tokens()', async () => {
|
||||
const tokenFrom = randomAddress();
|
||||
const tokenTo = randomAddress();
|
||||
const tokenAmount = new BigNumber(TRIGGER_FALLBACK_SUCCESS_AMOUNT);
|
||||
const receipt = await tokenSpender
|
||||
.transferERC20Tokens(token.address, tokenFrom, tokenTo, tokenAmount)
|
||||
.awaitTransactionSuccessAsync();
|
||||
verifyEventsFromLogs(
|
||||
receipt.logs,
|
||||
[
|
||||
{
|
||||
token: token.address,
|
||||
owner: tokenFrom,
|
||||
to: tokenTo,
|
||||
amount: tokenAmount,
|
||||
},
|
||||
],
|
||||
TestFixinTokenSpenderEvents.FallbackCalled,
|
||||
);
|
||||
return expect(tx).to.revertWith(new RawRevertError(hexUtils.leftPad(0)));
|
||||
});
|
||||
|
||||
it('transferERC20Tokens() allows extra data after true', async () => {
|
||||
@@ -185,15 +142,7 @@ blockchainTests.resets('FixinTokenSpender', env => {
|
||||
const tx = tokenSpender
|
||||
.transferERC20Tokens(token.address, tokenFrom, tokenTo, tokenAmount)
|
||||
.awaitTransactionSuccessAsync();
|
||||
return expect(tx).to.revertWith(
|
||||
new ZeroExRevertErrors.Spender.SpenderERC20TransferFromFailedError(
|
||||
token.address,
|
||||
tokenFrom,
|
||||
tokenTo,
|
||||
tokenAmount,
|
||||
hexUtils.leftPad(EXTRA_RETURN_FALSE_AMOUNT, 64),
|
||||
),
|
||||
);
|
||||
return expect(tx).to.revertWith(new RawRevertError(hexUtils.leftPad(EXTRA_RETURN_FALSE_AMOUNT, 64)));
|
||||
});
|
||||
|
||||
it('transferERC20Tokens() cannot call self', async () => {
|
||||
@@ -206,73 +155,6 @@ blockchainTests.resets('FixinTokenSpender', env => {
|
||||
.awaitTransactionSuccessAsync();
|
||||
return expect(tx).to.revertWith('FixinTokenSpender/CANNOT_INVOKE_SELF');
|
||||
});
|
||||
|
||||
it('calls transferFrom() directly for a greedy token with allowance', async () => {
|
||||
const tokenFrom = randomAddress();
|
||||
const tokenTo = randomAddress();
|
||||
const tokenAmount = new BigNumber(123456);
|
||||
await greedyToken.approveAs(tokenFrom, tokenSpender.address, tokenAmount).awaitTransactionSuccessAsync();
|
||||
|
||||
const receipt = await tokenSpender
|
||||
.transferERC20Tokens(greedyToken.address, tokenFrom, tokenTo, tokenAmount)
|
||||
.awaitTransactionSuccessAsync();
|
||||
// Because there is an allowance set, we will call transferFrom()
|
||||
// directly, which succeds and emits an event.
|
||||
verifyEventsFromLogs(
|
||||
receipt.logs,
|
||||
[
|
||||
{
|
||||
sender: tokenSpender.address,
|
||||
from: tokenFrom,
|
||||
to: tokenTo,
|
||||
amount: tokenAmount,
|
||||
},
|
||||
], // No events.
|
||||
TestTokenSpenderERC20TokenEvents.TransferFromCalled,
|
||||
);
|
||||
});
|
||||
|
||||
it('calls only the fallback for a greedy token with no allowance', async () => {
|
||||
const tokenFrom = randomAddress();
|
||||
const tokenTo = randomAddress();
|
||||
const tokenAmount = new BigNumber(TRIGGER_FALLBACK_SUCCESS_AMOUNT);
|
||||
|
||||
const receipt = await tokenSpender
|
||||
.transferERC20Tokens(greedyToken.address, tokenFrom, tokenTo, tokenAmount)
|
||||
.awaitTransactionSuccessAsync();
|
||||
// Because this is a greedy token and there is no allowance set, transferFrom()
|
||||
// will not be called before hitting the fallback, which only emits an event
|
||||
// in the test contract.
|
||||
verifyEventsFromLogs(
|
||||
receipt.logs,
|
||||
[], // No events.
|
||||
TestTokenSpenderERC20TokenEvents.TransferFromCalled,
|
||||
);
|
||||
verifyEventsFromLogs(
|
||||
receipt.logs,
|
||||
[
|
||||
{
|
||||
token: greedyToken.address,
|
||||
owner: tokenFrom,
|
||||
to: tokenTo,
|
||||
amount: tokenAmount,
|
||||
},
|
||||
],
|
||||
TestFixinTokenSpenderEvents.FallbackCalled,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isTokenPossiblyGreedy()', () => {
|
||||
it('returns true for greedy token', async () => {
|
||||
const isGreedy = await tokenSpender.isTokenPossiblyGreedy(greedyToken.address).callAsync();
|
||||
expect(isGreedy).to.eq(true);
|
||||
});
|
||||
|
||||
it('returns false for non-greedy token', async () => {
|
||||
const isGreedy = await tokenSpender.isTokenPossiblyGreedy(token.address).callAsync();
|
||||
expect(isGreedy).to.eq(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getSpendableERC20BalanceOf()', () => {
|
||||
|
@@ -8,11 +8,9 @@ import { artifacts } from './artifacts';
|
||||
import { abis } from './utils/abis';
|
||||
import { deployFullFeaturesAsync, FullFeatures } from './utils/migration';
|
||||
import {
|
||||
AllowanceTargetContract,
|
||||
IMetaTransactionsFeatureContract,
|
||||
INativeOrdersFeatureContract,
|
||||
IOwnableFeatureContract,
|
||||
ITokenSpenderFeatureContract,
|
||||
ITransformERC20FeatureContract,
|
||||
TestFullMigrationContract,
|
||||
ZeroExContract,
|
||||
@@ -69,10 +67,6 @@ blockchainTests.resets('Full migration', env => {
|
||||
});
|
||||
|
||||
const FEATURE_FNS = {
|
||||
TokenSpender: {
|
||||
contractType: ITokenSpenderFeatureContract,
|
||||
fns: ['_spendERC20Tokens'],
|
||||
},
|
||||
TransformERC20: {
|
||||
contractType: ITransformERC20FeatureContract,
|
||||
fns: [
|
||||
@@ -207,27 +201,6 @@ blockchainTests.resets('Full migration', env => {
|
||||
});
|
||||
}
|
||||
|
||||
describe("TokenSpender's allowance target", () => {
|
||||
let allowanceTarget: AllowanceTargetContract;
|
||||
|
||||
before(async () => {
|
||||
const contract = new ITokenSpenderFeatureContract(zeroEx.address, env.provider, env.txDefaults);
|
||||
allowanceTarget = new AllowanceTargetContract(
|
||||
await contract.getAllowanceTarget().callAsync(),
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
);
|
||||
});
|
||||
|
||||
it('is owned by owner', async () => {
|
||||
return expect(allowanceTarget.owner().callAsync()).to.become(owner);
|
||||
});
|
||||
|
||||
it('Proxy is authorized', async () => {
|
||||
return expect(allowanceTarget.authorized(zeroEx.address).callAsync()).to.become(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('TransformERC20', () => {
|
||||
let feature: ITransformERC20FeatureContract;
|
||||
|
||||
|
@@ -52,7 +52,7 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
||||
let singleProtocolFee: BigNumber;
|
||||
|
||||
const GAS_PRICE = 1337;
|
||||
const TEST_BRIDGE_SOURCE = 12345678;
|
||||
const TEST_BRIDGE_SOURCE = hexUtils.random(32);
|
||||
const HIGH_BIT = new BigNumber(2).pow(255);
|
||||
const REVERT_AMOUNT = new BigNumber('0xdeadbeef');
|
||||
|
||||
|
@@ -4,11 +4,10 @@
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
export * from '../test/generated-wrappers/affiliate_fee_transformer';
|
||||
export * from '../test/generated-wrappers/allowance_target';
|
||||
export * from '../test/generated-wrappers/batch_fill_native_orders_feature';
|
||||
export * from '../test/generated-wrappers/bootstrap_feature';
|
||||
export * from '../test/generated-wrappers/bridge_adapter';
|
||||
export * from '../test/generated-wrappers/bridge_source';
|
||||
export * from '../test/generated-wrappers/bridge_protocols';
|
||||
export * from '../test/generated-wrappers/curve_liquidity_provider';
|
||||
export * from '../test/generated-wrappers/fee_collector';
|
||||
export * from '../test/generated-wrappers/fee_collector_controller';
|
||||
@@ -20,7 +19,6 @@ export * from '../test/generated-wrappers/fixin_reentrancy_guard';
|
||||
export * from '../test/generated-wrappers/fixin_token_spender';
|
||||
export * from '../test/generated-wrappers/flash_wallet';
|
||||
export * from '../test/generated-wrappers/full_migration';
|
||||
export * from '../test/generated-wrappers/i_allowance_target';
|
||||
export * from '../test/generated-wrappers/i_batch_fill_native_orders_feature';
|
||||
export * from '../test/generated-wrappers/i_bootstrap_feature';
|
||||
export * from '../test/generated-wrappers/i_bridge_adapter';
|
||||
@@ -37,6 +35,7 @@ export * from '../test/generated-wrappers/i_multiplex_feature';
|
||||
export * from '../test/generated-wrappers/i_native_orders_events';
|
||||
export * from '../test/generated-wrappers/i_native_orders_feature';
|
||||
export * from '../test/generated-wrappers/i_ownable_feature';
|
||||
export * from '../test/generated-wrappers/i_pancake_swap_feature';
|
||||
export * from '../test/generated-wrappers/i_simple_function_registry_feature';
|
||||
export * from '../test/generated-wrappers/i_staking';
|
||||
export * from '../test/generated-wrappers/i_test_simple_function_registry_feature';
|
||||
@@ -66,9 +65,7 @@ export * from '../test/generated-wrappers/lib_signature';
|
||||
export * from '../test/generated-wrappers/lib_signature_rich_errors';
|
||||
export * from '../test/generated-wrappers/lib_simple_function_registry_rich_errors';
|
||||
export * from '../test/generated-wrappers/lib_simple_function_registry_storage';
|
||||
export * from '../test/generated-wrappers/lib_spender_rich_errors';
|
||||
export * from '../test/generated-wrappers/lib_storage';
|
||||
export * from '../test/generated-wrappers/lib_token_spender_storage';
|
||||
export * from '../test/generated-wrappers/lib_transform_erc20_rich_errors';
|
||||
export * from '../test/generated-wrappers/lib_transform_erc20_storage';
|
||||
export * from '../test/generated-wrappers/lib_wallet_rich_errors';
|
||||
@@ -86,9 +83,9 @@ export * from '../test/generated-wrappers/mixin_dodo_v2';
|
||||
export * from '../test/generated-wrappers/mixin_kyber';
|
||||
export * from '../test/generated-wrappers/mixin_m_stable';
|
||||
export * from '../test/generated-wrappers/mixin_mooniswap';
|
||||
export * from '../test/generated-wrappers/mixin_nerve';
|
||||
export * from '../test/generated-wrappers/mixin_oasis';
|
||||
export * from '../test/generated-wrappers/mixin_shell';
|
||||
export * from '../test/generated-wrappers/mixin_sushiswap';
|
||||
export * from '../test/generated-wrappers/mixin_uniswap';
|
||||
export * from '../test/generated-wrappers/mixin_uniswap_v2';
|
||||
export * from '../test/generated-wrappers/mixin_zero_ex_bridge';
|
||||
@@ -100,6 +97,7 @@ export * from '../test/generated-wrappers/native_orders_info';
|
||||
export * from '../test/generated-wrappers/native_orders_protocol_fees';
|
||||
export * from '../test/generated-wrappers/native_orders_settlement';
|
||||
export * from '../test/generated-wrappers/ownable_feature';
|
||||
export * from '../test/generated-wrappers/pancake_swap_feature';
|
||||
export * from '../test/generated-wrappers/pay_taker_transformer';
|
||||
export * from '../test/generated-wrappers/permissionless_transformer_deployer';
|
||||
export * from '../test/generated-wrappers/positive_slippage_fee_transformer';
|
||||
@@ -132,7 +130,6 @@ export * from '../test/generated-wrappers/test_rfq_origin_registration';
|
||||
export * from '../test/generated-wrappers/test_simple_function_registry_feature_impl1';
|
||||
export * from '../test/generated-wrappers/test_simple_function_registry_feature_impl2';
|
||||
export * from '../test/generated-wrappers/test_staking';
|
||||
export * from '../test/generated-wrappers/test_token_spender';
|
||||
export * from '../test/generated-wrappers/test_token_spender_erc20_token';
|
||||
export * from '../test/generated-wrappers/test_transform_erc20';
|
||||
export * from '../test/generated-wrappers/test_transformer_base';
|
||||
@@ -141,7 +138,6 @@ export * from '../test/generated-wrappers/test_transformer_host';
|
||||
export * from '../test/generated-wrappers/test_weth';
|
||||
export * from '../test/generated-wrappers/test_weth_transformer_host';
|
||||
export * from '../test/generated-wrappers/test_zero_ex_feature';
|
||||
export * from '../test/generated-wrappers/token_spender_feature';
|
||||
export * from '../test/generated-wrappers/transform_erc20_feature';
|
||||
export * from '../test/generated-wrappers/transformer';
|
||||
export * from '../test/generated-wrappers/transformer_deployer';
|
||||
|
@@ -20,7 +20,6 @@
|
||||
"generated-artifacts/INativeOrdersFeature.json",
|
||||
"generated-artifacts/IOwnableFeature.json",
|
||||
"generated-artifacts/ISimpleFunctionRegistryFeature.json",
|
||||
"generated-artifacts/ITokenSpenderFeature.json",
|
||||
"generated-artifacts/ITransformERC20Feature.json",
|
||||
"generated-artifacts/IZeroEx.json",
|
||||
"generated-artifacts/InitialMigration.json",
|
||||
@@ -33,16 +32,14 @@
|
||||
"generated-artifacts/PayTakerTransformer.json",
|
||||
"generated-artifacts/PositiveSlippageFeeTransformer.json",
|
||||
"generated-artifacts/SimpleFunctionRegistryFeature.json",
|
||||
"generated-artifacts/TokenSpenderFeature.json",
|
||||
"generated-artifacts/TransformERC20Feature.json",
|
||||
"generated-artifacts/WethTransformer.json",
|
||||
"generated-artifacts/ZeroEx.json",
|
||||
"test/generated-artifacts/AffiliateFeeTransformer.json",
|
||||
"test/generated-artifacts/AllowanceTarget.json",
|
||||
"test/generated-artifacts/BatchFillNativeOrdersFeature.json",
|
||||
"test/generated-artifacts/BootstrapFeature.json",
|
||||
"test/generated-artifacts/BridgeAdapter.json",
|
||||
"test/generated-artifacts/BridgeSource.json",
|
||||
"test/generated-artifacts/BridgeProtocols.json",
|
||||
"test/generated-artifacts/CurveLiquidityProvider.json",
|
||||
"test/generated-artifacts/FeeCollector.json",
|
||||
"test/generated-artifacts/FeeCollectorController.json",
|
||||
@@ -54,7 +51,6 @@
|
||||
"test/generated-artifacts/FixinTokenSpender.json",
|
||||
"test/generated-artifacts/FlashWallet.json",
|
||||
"test/generated-artifacts/FullMigration.json",
|
||||
"test/generated-artifacts/IAllowanceTarget.json",
|
||||
"test/generated-artifacts/IBatchFillNativeOrdersFeature.json",
|
||||
"test/generated-artifacts/IBootstrapFeature.json",
|
||||
"test/generated-artifacts/IBridgeAdapter.json",
|
||||
@@ -71,6 +67,7 @@
|
||||
"test/generated-artifacts/INativeOrdersEvents.json",
|
||||
"test/generated-artifacts/INativeOrdersFeature.json",
|
||||
"test/generated-artifacts/IOwnableFeature.json",
|
||||
"test/generated-artifacts/IPancakeSwapFeature.json",
|
||||
"test/generated-artifacts/ISimpleFunctionRegistryFeature.json",
|
||||
"test/generated-artifacts/IStaking.json",
|
||||
"test/generated-artifacts/ITestSimpleFunctionRegistryFeature.json",
|
||||
@@ -100,9 +97,7 @@
|
||||
"test/generated-artifacts/LibSignatureRichErrors.json",
|
||||
"test/generated-artifacts/LibSimpleFunctionRegistryRichErrors.json",
|
||||
"test/generated-artifacts/LibSimpleFunctionRegistryStorage.json",
|
||||
"test/generated-artifacts/LibSpenderRichErrors.json",
|
||||
"test/generated-artifacts/LibStorage.json",
|
||||
"test/generated-artifacts/LibTokenSpenderStorage.json",
|
||||
"test/generated-artifacts/LibTransformERC20RichErrors.json",
|
||||
"test/generated-artifacts/LibTransformERC20Storage.json",
|
||||
"test/generated-artifacts/LibWalletRichErrors.json",
|
||||
@@ -120,9 +115,9 @@
|
||||
"test/generated-artifacts/MixinKyber.json",
|
||||
"test/generated-artifacts/MixinMStable.json",
|
||||
"test/generated-artifacts/MixinMooniswap.json",
|
||||
"test/generated-artifacts/MixinNerve.json",
|
||||
"test/generated-artifacts/MixinOasis.json",
|
||||
"test/generated-artifacts/MixinShell.json",
|
||||
"test/generated-artifacts/MixinSushiswap.json",
|
||||
"test/generated-artifacts/MixinUniswap.json",
|
||||
"test/generated-artifacts/MixinUniswapV2.json",
|
||||
"test/generated-artifacts/MixinZeroExBridge.json",
|
||||
@@ -134,6 +129,7 @@
|
||||
"test/generated-artifacts/NativeOrdersProtocolFees.json",
|
||||
"test/generated-artifacts/NativeOrdersSettlement.json",
|
||||
"test/generated-artifacts/OwnableFeature.json",
|
||||
"test/generated-artifacts/PancakeSwapFeature.json",
|
||||
"test/generated-artifacts/PayTakerTransformer.json",
|
||||
"test/generated-artifacts/PermissionlessTransformerDeployer.json",
|
||||
"test/generated-artifacts/PositiveSlippageFeeTransformer.json",
|
||||
@@ -166,7 +162,6 @@
|
||||
"test/generated-artifacts/TestSimpleFunctionRegistryFeatureImpl1.json",
|
||||
"test/generated-artifacts/TestSimpleFunctionRegistryFeatureImpl2.json",
|
||||
"test/generated-artifacts/TestStaking.json",
|
||||
"test/generated-artifacts/TestTokenSpender.json",
|
||||
"test/generated-artifacts/TestTokenSpenderERC20Token.json",
|
||||
"test/generated-artifacts/TestTransformERC20.json",
|
||||
"test/generated-artifacts/TestTransformerBase.json",
|
||||
@@ -175,7 +170,6 @@
|
||||
"test/generated-artifacts/TestWeth.json",
|
||||
"test/generated-artifacts/TestWethTransformerHost.json",
|
||||
"test/generated-artifacts/TestZeroExFeature.json",
|
||||
"test/generated-artifacts/TokenSpenderFeature.json",
|
||||
"test/generated-artifacts/TransformERC20Feature.json",
|
||||
"test/generated-artifacts/Transformer.json",
|
||||
"test/generated-artifacts/TransformerDeployer.json",
|
||||
|
Reference in New Issue
Block a user