UniswapFeature (#2703)
* Minimal Uniswap accessor. * Add comments * Safe math * mainnet gas benchmark * Assembler Uniswap * Selectors and addresses * Fix bugs in ABI encoders * Typo * AsmUniswap test * Fix wantAmount computation * Golfing * Bypass AllowanceTarget * Generalized asm uniswapper * Implement ordering * Fix pair computation * #6 Golfing Iron * Remove 'to' argument (saves 377 gas) * New contract api * `@0x/contracts-zero-ex`: Add `UniswapFeature` * `@0x/contract-artifacts`: Regenerate artifacts * `@0x/contract-wrappers`: Regenerate wrappers * `@0x/asset-swapper`: Add Uniswap VIP support. `@0x/asset-swapper`: Add `includeSources` support. * `@0x/contracts-zero-ex`: Fix misleading comments in `UniswapFeature`. `@0x/asset-swapper`: Fix linter errors. * `@0x/asset-swapper`: Fix source filter bugs. * `@0x/contracts-zero-ex`: `UniswapFeature`: Reduce calldata size for AllowanceTarget call `@0x/asset-swapper`: Fix failing test. * `@0x/contracts-zero-ex`: Fix ETH buy tokens not being normalized to WETH. * `@0x/asset-swapper`: Fix multi-hop weirdness with source filters. * `@0x/asset-swapper`: Fix failing test. * `@0x/asset-swapper`: Really fix that broken AS test. * `@0x/asset-swapper`: use filter objects instead of source array for valid buy and sell sources/ * `@0x/asset-swapper`: Move some source filtering logic into the sampler operations. * `@0x/contracts-zero-ex`: Address PR feedback * `@0x/contracts-zero-ex`: Fix feature version bug. * `@0x/asset-swapper`: Did I actually fix AS tests this time? Who knows. Co-authored-by: Remco Bloemen <remco@0x.org> Co-authored-by: Michael Zhu <mchl.zhu.96@gmail.com> Co-authored-by: Lawrence Forman <me@merklejerk.com>
This commit is contained in:
@@ -49,6 +49,14 @@
|
||||
{
|
||||
"note": "Add updated Kyber and Mooniswap rollup to FQT",
|
||||
"pr": 2692
|
||||
},
|
||||
{
|
||||
"note": "Add `UniswapFeature`",
|
||||
"pr": 2703
|
||||
},
|
||||
{
|
||||
"note": "Fix versioning (`_encodeVersion()`) bug",
|
||||
"pr": 2703
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@@ -25,6 +25,7 @@ import "./features/ITokenSpenderFeature.sol";
|
||||
import "./features/ISignatureValidatorFeature.sol";
|
||||
import "./features/ITransformERC20Feature.sol";
|
||||
import "./features/IMetaTransactionsFeature.sol";
|
||||
import "./features/IUniswapFeature.sol";
|
||||
|
||||
|
||||
/// @dev Interface for a fully featured Exchange Proxy.
|
||||
@@ -34,7 +35,8 @@ interface IZeroEx is
|
||||
ITokenSpenderFeature,
|
||||
ISignatureValidatorFeature,
|
||||
ITransformERC20Feature,
|
||||
IMetaTransactionsFeature
|
||||
IMetaTransactionsFeature,
|
||||
IUniswapFeature
|
||||
{
|
||||
// solhint-disable state-visibility
|
||||
|
||||
|
43
contracts/zero-ex/contracts/src/features/IUniswapFeature.sol
Normal file
43
contracts/zero-ex/contracts/src/features/IUniswapFeature.sol
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
|
||||
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/IERC20TokenV06.sol";
|
||||
|
||||
|
||||
/// @dev VIP uniswap fill functions.
|
||||
interface IUniswapFeature {
|
||||
|
||||
/// @dev Efficiently sell directly to uniswap/sushiswap.
|
||||
/// @param tokens Sell path.
|
||||
/// @param sellAmount of `tokens[0]` Amount to sell.
|
||||
/// @param minBuyAmount Minimum amount of `tokens[-1]` to buy.
|
||||
/// @param isSushi Use sushiswap if true.
|
||||
/// @return buyAmount Amount of `tokens[-1]` bought.
|
||||
function sellToUniswap(
|
||||
IERC20TokenV06[] calldata tokens,
|
||||
uint256 sellAmount,
|
||||
uint256 minBuyAmount,
|
||||
bool isSushi
|
||||
)
|
||||
external
|
||||
payable
|
||||
returns (uint256 buyAmount);
|
||||
}
|
366
contracts/zero-ex/contracts/src/features/UniswapFeature.sol
Normal file
366
contracts/zero-ex/contracts/src/features/UniswapFeature.sol
Normal file
@@ -0,0 +1,366 @@
|
||||
/*
|
||||
|
||||
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/IERC20TokenV06.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
|
||||
import "../migrations/LibMigrate.sol";
|
||||
import "../external/IAllowanceTarget.sol";
|
||||
import "../fixins/FixinCommon.sol";
|
||||
import "./IFeature.sol";
|
||||
import "./IUniswapFeature.sol";
|
||||
|
||||
|
||||
/// @dev VIP uniswap fill functions.
|
||||
contract UniswapFeature is
|
||||
IFeature,
|
||||
IUniswapFeature,
|
||||
FixinCommon
|
||||
{
|
||||
/// @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, 0, 0);
|
||||
/// @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;
|
||||
// 0xFF + address of the (Sushiswap) UniswapV2Factory contract.
|
||||
uint256 constant private FF_SUSHISWAP_FACTORY = 0xFFC0AEe478e3658e2610c5F7A4A2E1777cE9e4f2Ac0000000000000000000000;
|
||||
// Init code hash of the UniswapV2Pair contract.
|
||||
uint256 constant private UNISWAP_PAIR_INIT_CODE_HASH = 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f;
|
||||
// Init code hash of the (Sushiswap) UniswapV2Pair 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;
|
||||
// ETH pseudo-token address.
|
||||
uint256 constant private ETH_TOKEN_ADDRESS_32 = 0x000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee;
|
||||
// Maximum token quantity that can be swapped against the UniswapV2Pair 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 UNISWAP_PAIR_RESERVES_CALL_SELECTOR_32 = 0x0902f1ac00000000000000000000000000000000000000000000000000000000;
|
||||
// bytes4(keccak256("swap(uint256,uint256,address,bytes)"))
|
||||
uint256 constant private UNISWAP_PAIR_SWAP_CALL_SELECTOR_32 = 0x022c0d9f00000000000000000000000000000000000000000000000000000000;
|
||||
// bytes4(keccak256("transferFrom(address,address,uint256)"))
|
||||
uint256 constant private TRANSFER_FROM_CALL_SELECTOR_32 = 0x23b872dd00000000000000000000000000000000000000000000000000000000;
|
||||
// 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 weth The WETH contract.
|
||||
/// @param allowanceTarget The AllowanceTarget contract.
|
||||
constructor(IEtherTokenV06 weth, IAllowanceTarget allowanceTarget) public {
|
||||
WETH = weth;
|
||||
ALLOWANCE_TARGET = allowanceTarget;
|
||||
}
|
||||
|
||||
/// @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.sellToUniswap.selector);
|
||||
return LibMigrate.MIGRATE_SUCCESS;
|
||||
}
|
||||
|
||||
/// @dev Efficiently sell directly to uniswap/sushiswap.
|
||||
/// @param tokens Sell path.
|
||||
/// @param sellAmount of `tokens[0]` Amount to sell.
|
||||
/// @param minBuyAmount Minimum amount of `tokens[-1]` to buy.
|
||||
/// @param isSushi Use sushiswap if true.
|
||||
/// @return buyAmount Amount of `tokens[-1]` bought.
|
||||
function sellToUniswap(
|
||||
IERC20TokenV06[] calldata tokens,
|
||||
uint256 sellAmount,
|
||||
uint256 minBuyAmount,
|
||||
bool isSushi
|
||||
)
|
||||
external
|
||||
payable
|
||||
override
|
||||
returns (uint256 buyAmount)
|
||||
{
|
||||
require(tokens.length > 1, "UniswapFeature/InvalidTokensLength");
|
||||
{
|
||||
// Load immutables onto the stack.
|
||||
IEtherTokenV06 weth = WETH;
|
||||
IAllowanceTarget allowanceTarget = ALLOWANCE_TARGET;
|
||||
|
||||
// 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) == isSushi
|
||||
mstore(0xA20, isSushi)
|
||||
// mload(0xA40) == WETH
|
||||
mstore(0xA40, weth)
|
||||
// mload(0xA60) == ALLOWANCE_TARGET
|
||||
mstore(0xA60, allowanceTarget)
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
switch eq(sellToken, ETH_TOKEN_ADDRESS_32)
|
||||
case 0 {
|
||||
// For the first pair we need to transfer sellTokens into the
|
||||
// pair contract using `AllowanceTarget.executeCall()`
|
||||
mstore(0xB00, ALLOWANCE_TARGET_EXECUTE_CALL_SELECTOR_32)
|
||||
mstore(0xB04, sellToken)
|
||||
mstore(0xB24, 0x40)
|
||||
mstore(0xB44, 0x64)
|
||||
mstore(0xB64, TRANSFER_FROM_CALL_SELECTOR_32)
|
||||
mstore(0xB68, caller())
|
||||
mstore(0xB88, pair)
|
||||
mstore(0xBA8, sellAmount)
|
||||
if iszero(call(gas(), mload(0xA60), 0, 0xB00, 0xC8, 0x00, 0x0)) {
|
||||
bubbleRevert()
|
||||
}
|
||||
}
|
||||
default {
|
||||
// If selling ETH, we need to wrap it to WETH and transfer to the
|
||||
// pair contract.
|
||||
if iszero(eq(callvalue(), sellAmount)) {
|
||||
revert(0, 0)
|
||||
}
|
||||
sellToken := mload(0xA40)// Re-assign to WETH
|
||||
// Call `WETH.deposit{value: sellAmount}()`
|
||||
mstore(0xB00, WETH_DEPOSIT_CALL_SELECTOR_32)
|
||||
if iszero(call(gas(), sellToken, sellAmount, 0xB00, 0x4, 0x00, 0x0)) {
|
||||
bubbleRevert()
|
||||
}
|
||||
// Call `WETH.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 UniswapV2Pair 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, UNISWAP_PAIR_RESERVES_CALL_SELECTOR_32)
|
||||
if iszero(staticcall(gas(), pair, 0xB00, 0x4, 0xC00, 0x40)) {
|
||||
bubbleRevert()
|
||||
}
|
||||
|
||||
// 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 ETH back.
|
||||
switch eq(buyToken, ETH_TOKEN_ADDRESS_32)
|
||||
case 0 {
|
||||
receiver := caller()
|
||||
}
|
||||
default {
|
||||
receiver := address()
|
||||
}
|
||||
}
|
||||
|
||||
// Call pair.swap()
|
||||
mstore(0xB00, UNISWAP_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 ETH, unwrap the WETH first
|
||||
if eq(buyToken, ETH_TOKEN_ADDRESS_32) {
|
||||
// Call `WETH.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 ETH 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 ETH pseudo-token addresses to WETH.
|
||||
function normalizeToken(token) -> normalized {
|
||||
normalized := token
|
||||
// Translate ETH pseudo-tokens to WETH.
|
||||
if eq(token, ETH_TOKEN_ADDRESS_32) {
|
||||
normalized := mload(0xA40)
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the address of the UniswapV2Pair contract given two
|
||||
// tokens.
|
||||
function computePairAddress(tokenA, tokenB) -> pair {
|
||||
// Convert ETH pseudo-token addresses to WETH.
|
||||
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(UNISWAP_FACTORY_ADDRESS),
|
||||
// keccak256(abi.encodePacked(
|
||||
// tokenA < tokenB ? tokenA : tokenB,
|
||||
// tokenA < tokenB ? tokenB : tokenA,
|
||||
// )),
|
||||
// bytes32(UNISWAP_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) // isSushi
|
||||
case 0 {
|
||||
mstore(0xB00, FF_UNISWAP_FACTORY)
|
||||
mstore(0xB15, salt)
|
||||
mstore(0xB35, UNISWAP_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())
|
||||
}
|
||||
}
|
||||
|
||||
// Revert if we bought too little.
|
||||
// TODO: replace with rich revert?
|
||||
require(buyAmount >= minBuyAmount, "UniswapFeature/UnderBought");
|
||||
}
|
||||
}
|
@@ -81,6 +81,6 @@ abstract contract FixinCommon {
|
||||
pure
|
||||
returns (uint256 encodedVersion)
|
||||
{
|
||||
return (major << 64) | (minor << 32) | revision;
|
||||
return (uint256(major) << 64) | (uint256(minor) << 32) | uint256(revision);
|
||||
}
|
||||
}
|
||||
|
@@ -29,6 +29,8 @@ library LibERC20Transformer {
|
||||
|
||||
/// @dev ETH pseudo-token address.
|
||||
address constant internal ETH_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
|
||||
/// @dev ETH pseudo-token.
|
||||
IERC20TokenV06 constant internal ETH_TOKEN = IERC20TokenV06(ETH_TOKEN_ADDRESS);
|
||||
/// @dev Return value indicating success in `IERC20Transformer.transform()`.
|
||||
/// This is just `keccak256('TRANSFORMER_SUCCESS')`.
|
||||
bytes4 constant internal TRANSFORMER_SUCCESS = 0x13c9929e;
|
||||
|
@@ -41,7 +41,7 @@
|
||||
"config": {
|
||||
"publicInterfaceContracts": "IZeroEx,ZeroEx,FullMigration,InitialMigration,IFlashWallet,IAllowanceTarget,IERC20Transformer,IOwnableFeature,ISimpleFunctionRegistryFeature,ITokenSpenderFeature,ITransformERC20Feature,FillQuoteTransformer,PayTakerTransformer,WethTransformer,OwnableFeature,SimpleFunctionRegistryFeature,TransformERC20Feature,TokenSpenderFeature,AffiliateFeeTransformer,SignatureValidatorFeature,MetaTransactionsFeature,LogMetadataTransformer,BridgeAdapter",
|
||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
|
||||
"abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|AllowanceTarget|BootstrapFeature|BridgeAdapter|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinReentrancyGuard|FlashWallet|FullMigration|IAllowanceTarget|IBootstrapFeature|IBridgeAdapter|IERC20Bridge|IERC20Transformer|IExchange|IFeature|IFlashWallet|IGasToken|IMetaTransactionsFeature|IOwnableFeature|ISignatureValidatorFeature|ISimpleFunctionRegistryFeature|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC20Transformer|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignatureRichErrors|LibSignedCallData|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibSpenderRichErrors|LibStorage|LibTokenSpenderStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LogMetadataTransformer|MetaTransactionsFeature|MixinAdapterAddresses|MixinBalancer|MixinCurve|MixinKyber|MixinMStable|MixinMooniswap|MixinOasis|MixinUniswap|MixinUniswapV2|MixinZeroExBridge|OwnableFeature|PayTakerTransformer|SignatureValidatorFeature|SimpleFunctionRegistryFeature|TestCallTarget|TestDelegateCaller|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFullMigration|TestInitialMigration|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC20Token|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestTokenSpender|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestWeth|TestWethTransformerHost|TestZeroExFeature|TokenSpenderFeature|TransformERC20Feature|Transformer|TransformerDeployer|WethTransformer|ZeroEx).json"
|
||||
"abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|AllowanceTarget|BootstrapFeature|BridgeAdapter|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinReentrancyGuard|FlashWallet|FullMigration|IAllowanceTarget|IBootstrapFeature|IBridgeAdapter|IERC20Bridge|IERC20Transformer|IExchange|IFeature|IFlashWallet|IGasToken|IMetaTransactionsFeature|IOwnableFeature|ISignatureValidatorFeature|ISimpleFunctionRegistryFeature|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC20Transformer|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignatureRichErrors|LibSignedCallData|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibSpenderRichErrors|LibStorage|LibTokenSpenderStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LogMetadataTransformer|MetaTransactionsFeature|MixinAdapterAddresses|MixinBalancer|MixinCurve|MixinKyber|MixinMStable|MixinMooniswap|MixinOasis|MixinUniswap|MixinUniswapV2|MixinZeroExBridge|OwnableFeature|PayTakerTransformer|SignatureValidatorFeature|SimpleFunctionRegistryFeature|TestCallTarget|TestDelegateCaller|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFullMigration|TestInitialMigration|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC20Token|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestTokenSpender|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestWeth|TestWethTransformerHost|TestZeroExFeature|TokenSpenderFeature|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|WethTransformer|ZeroEx).json"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@@ -32,6 +32,7 @@ import * as ISimpleFunctionRegistryFeature from '../test/generated-artifacts/ISi
|
||||
import * as ITestSimpleFunctionRegistryFeature from '../test/generated-artifacts/ITestSimpleFunctionRegistryFeature.json';
|
||||
import * as ITokenSpenderFeature from '../test/generated-artifacts/ITokenSpenderFeature.json';
|
||||
import * as ITransformERC20Feature from '../test/generated-artifacts/ITransformERC20Feature.json';
|
||||
import * as IUniswapFeature from '../test/generated-artifacts/IUniswapFeature.json';
|
||||
import * as IZeroEx from '../test/generated-artifacts/IZeroEx.json';
|
||||
import * as LibBootstrap from '../test/generated-artifacts/LibBootstrap.json';
|
||||
import * as LibCommonRichErrors from '../test/generated-artifacts/LibCommonRichErrors.json';
|
||||
@@ -96,6 +97,7 @@ import * as TokenSpenderFeature from '../test/generated-artifacts/TokenSpenderFe
|
||||
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';
|
||||
import * as UniswapFeature from '../test/generated-artifacts/UniswapFeature.json';
|
||||
import * as WethTransformer from '../test/generated-artifacts/WethTransformer.json';
|
||||
import * as ZeroEx from '../test/generated-artifacts/ZeroEx.json';
|
||||
export const artifacts = {
|
||||
@@ -124,12 +126,14 @@ export const artifacts = {
|
||||
ISimpleFunctionRegistryFeature: ISimpleFunctionRegistryFeature as ContractArtifact,
|
||||
ITokenSpenderFeature: ITokenSpenderFeature as ContractArtifact,
|
||||
ITransformERC20Feature: ITransformERC20Feature as ContractArtifact,
|
||||
IUniswapFeature: IUniswapFeature as ContractArtifact,
|
||||
MetaTransactionsFeature: MetaTransactionsFeature as ContractArtifact,
|
||||
OwnableFeature: OwnableFeature as ContractArtifact,
|
||||
SignatureValidatorFeature: SignatureValidatorFeature as ContractArtifact,
|
||||
SimpleFunctionRegistryFeature: SimpleFunctionRegistryFeature as ContractArtifact,
|
||||
TokenSpenderFeature: TokenSpenderFeature as ContractArtifact,
|
||||
TransformERC20Feature: TransformERC20Feature as ContractArtifact,
|
||||
UniswapFeature: UniswapFeature as ContractArtifact,
|
||||
LibSignedCallData: LibSignedCallData as ContractArtifact,
|
||||
FixinCommon: FixinCommon as ContractArtifact,
|
||||
FixinEIP712: FixinEIP712 as ContractArtifact,
|
||||
|
@@ -29,6 +29,7 @@ export * from '../test/generated-wrappers/i_simple_function_registry_feature';
|
||||
export * from '../test/generated-wrappers/i_test_simple_function_registry_feature';
|
||||
export * from '../test/generated-wrappers/i_token_spender_feature';
|
||||
export * from '../test/generated-wrappers/i_transform_erc20_feature';
|
||||
export * from '../test/generated-wrappers/i_uniswap_feature';
|
||||
export * from '../test/generated-wrappers/i_zero_ex';
|
||||
export * from '../test/generated-wrappers/initial_migration';
|
||||
export * from '../test/generated-wrappers/lib_bootstrap';
|
||||
@@ -94,5 +95,6 @@ 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';
|
||||
export * from '../test/generated-wrappers/uniswap_feature';
|
||||
export * from '../test/generated-wrappers/weth_transformer';
|
||||
export * from '../test/generated-wrappers/zero_ex';
|
||||
|
@@ -52,6 +52,7 @@
|
||||
"test/generated-artifacts/ITestSimpleFunctionRegistryFeature.json",
|
||||
"test/generated-artifacts/ITokenSpenderFeature.json",
|
||||
"test/generated-artifacts/ITransformERC20Feature.json",
|
||||
"test/generated-artifacts/IUniswapFeature.json",
|
||||
"test/generated-artifacts/IZeroEx.json",
|
||||
"test/generated-artifacts/InitialMigration.json",
|
||||
"test/generated-artifacts/LibBootstrap.json",
|
||||
@@ -117,6 +118,7 @@
|
||||
"test/generated-artifacts/TransformERC20Feature.json",
|
||||
"test/generated-artifacts/Transformer.json",
|
||||
"test/generated-artifacts/TransformerDeployer.json",
|
||||
"test/generated-artifacts/UniswapFeature.json",
|
||||
"test/generated-artifacts/WethTransformer.json",
|
||||
"test/generated-artifacts/ZeroEx.json"
|
||||
],
|
||||
|
Reference in New Issue
Block a user