diff --git a/contracts/core/exchange-wrappers/KyberNetworkWrapper.sol b/contracts/core/exchange-wrappers/KyberNetworkWrapper.sol new file mode 100644 index 000000000..042d01c82 --- /dev/null +++ b/contracts/core/exchange-wrappers/KyberNetworkWrapper.sol @@ -0,0 +1,220 @@ +/* + Copyright 2018 Set Labs Inc. + + 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.4.24; +pragma experimental "ABIEncoderV2"; + +import { SafeMath } from "zeppelin-solidity/contracts/math/SafeMath.sol"; +import { Authorizable } from "../../lib/Authorizable.sol"; +import { ERC20Wrapper as ERC20 } from "../../lib/ERC20Wrapper.sol"; +import { KyberNetworkProxyInterface } from "../../external/KyberNetwork/KyberNetworkProxyInterface.sol"; +import { LibBytes } from "../../external/0x/LibBytes.sol"; + + +/** + * @title KyberNetworkWrapper + * @author Set Protocol + * + * The KyberNetworkWrapper contract wrapper to interface with KyberNetwork for reserve liquidity + */ +contract KyberNetworkWrapper is + Authorizable +{ + using SafeMath for uint256; + using LibBytes for bytes; + + /* ============ State Variables ============ */ + + address public kyberNetworkProxy; + address public setTransferProxy; + + // ============ Structs ============ + + struct KyberTrade { + address sourceToken; + address destinationToken; + uint256 sourceTokenQuantity; + uint256 minimumConversionRate; + uint256 maxDestinationQuantity; + } + + /* ============ Constructor ============ */ + + /** + * Initialize exchange wrapper with required addresses to facilitate Kyber trades + * + * @param _kyberNetworkProxy KyberNetwork contract for filling orders + * @param _setTransferProxy Set Protocol transfer proxy contract + */ + constructor( + address _kyberNetworkProxy, + address _setTransferProxy + ) + public + Authorizable(2592000) // about 4 weeks + { + kyberNetworkProxy = _kyberNetworkProxy; + setTransferProxy = _setTransferProxy; + } + + /* ============ Public Functions ============ */ + + /** + * IExchangeWrapper interface delegate method. + * + * Parses and executes Kyber trades. Depending on conversion rate, Kyber trades may result in change. + * We currently pass change back to the issuance order maker, exploring how it can safely be passed to the taker. + * + * + * @param _maker Address of issuance order signer to conform to IExchangeWrapper + * @param _ Unused address of fillOrder caller to conform to IExchangeWrapper + * @param _tradeCount Amount of trades in exchange request + * @param _tradesData Byte string containing (multiple) Kyber trades + * @return address[] Array of token addresses traded for + * @return uint256[] Array of token amounts traded for + */ + function exchange( + address _maker, + address _, + uint256 _tradeCount, + bytes _tradesData + ) + external + onlyAuthorized + returns (address[], uint256[]) + { + address[] memory takerTokens = new address[](_tradeCount); + uint256[] memory takerAmounts = new uint256[](_tradeCount); + + uint256 scannedBytes = 0; + for (uint256 i = 0; i < _tradeCount; i++) { + // Parse Kyber trade of current offset + KyberTrade memory trade = parseKyberTrade( + _tradesData, + scannedBytes + ); + + // Execute the trade via the KyberNetworkProxy + takerTokens[i] = trade.destinationToken; + takerAmounts[i] = tradeOnKyberReserve( + trade, + _maker + ); + + // Update current bytes + scannedBytes = scannedBytes.add(160); + } + + return ( + takerTokens, + takerAmounts + ); + } + + /* ============ Private ============ */ + + /** + * Executes Kyber trade + * + * @param _trade Kyber trade parameter struct + * @param _maker Address of issuance order maker to pass change to + * @return address Address of set component to trade for + * @return uint256 Amount of set component received in trade + */ + function tradeOnKyberReserve( + KyberTrade memory _trade, + address _maker + ) + private + returns (uint256) + { + // Ensure the source token is allowed to be transferred by KyberNetworkProxy + ERC20.ensureAllowance( + _trade.sourceToken, + address(this), + kyberNetworkProxy, + _trade.sourceTokenQuantity + ); + + uint256 destinationTokenQuantity = KyberNetworkProxyInterface(kyberNetworkProxy).trade( + _trade.sourceToken, + _trade.sourceTokenQuantity, + _trade.destinationToken, + address(this), + _trade.maxDestinationQuantity, + _trade.minimumConversionRate, + 0 + ); + + // Transfer any unused issuance order maker token back to user + uint remainderSourceToken = ERC20.balanceOf(_trade.sourceToken, this); + if (remainderSourceToken > 0) { + ERC20.transfer( + _trade.sourceToken, + _maker, + remainderSourceToken + ); + } + + // Ensure the maker token is allowed to be transferred by Set TransferProxy + ERC20.ensureAllowance( + _trade.destinationToken, + address(this), + setTransferProxy, + destinationTokenQuantity + ); + + return destinationTokenQuantity; + } + + /* + * Parses the bytes array for a Kyber trade + * + * | Data | Location | + * |----------------------------|-------------------------------| + * | sourceToken | 0 | + * | destinationToken | 32 | + * | sourceTokenQuantity | 64 | + * | minimumConversionRate | 98 | + * | maxDestinationQuantity | 128 | + * + * @param _tradesData Byte array of (multiple) Kyber trades + * @param _offset Offset to start scanning for Kyber trade body + * @return KyberTrade KyberTrade struct + */ + function parseKyberTrade( + bytes _tradesData, + uint256 _offset + ) + private + pure + returns (KyberTrade memory) + { + KyberTrade memory trade; + + uint256 tradeDataStart = _tradesData.contentAddress().add(_offset); + + assembly { + mstore(trade, mload(tradeDataStart)) // sourceToken + mstore(add(trade, 32), mload(add(tradeDataStart, 32))) // destinationToken + mstore(add(trade, 64), mload(add(tradeDataStart, 64))) // sourceTokenQuantity + mstore(add(trade, 96), mload(add(tradeDataStart, 96))) // minimumConversionRate + mstore(add(trade, 128), mload(add(tradeDataStart, 128))) // maxDestinationQuantity + } + + return trade; + } +} diff --git a/contracts/core/exchange-wrappers/TakerWalletWrapper.sol b/contracts/core/exchange-wrappers/TakerWalletWrapper.sol index d2958182e..b7446580a 100644 --- a/contracts/core/exchange-wrappers/TakerWalletWrapper.sol +++ b/contracts/core/exchange-wrappers/TakerWalletWrapper.sol @@ -62,13 +62,15 @@ contract TakerWalletWrapper is * IExchange interface delegate method. * Parses taker wallet orders and transfers tokens from taker's wallet. * - * @param _taker Taker wallet address - * @param _orderCount Amount of orders in exchange request - * @param _ordersData Encoded taker wallet order data - * @return Array of token addresses executed in orders - * @return Array of token amounts executed in orders + * @param _ Unused address of issuance order signer to conform to IExchangeWrapper + * @param _taker Taker wallet address + * @param _orderCount Amount of orders in exchange request + * @param _ordersData Encoded taker wallet order data + * @return address[] Array of token addresses executed in orders + * @return uint256[] Array of token amounts executed in orders */ function exchange( + address _, address _taker, uint256 _orderCount, bytes _ordersData diff --git a/contracts/core/exchange-wrappers/ZeroExExchangeWrapper.sol b/contracts/core/exchange-wrappers/ZeroExExchangeWrapper.sol index 429a13a32..6f4e0c159 100644 --- a/contracts/core/exchange-wrappers/ZeroExExchangeWrapper.sol +++ b/contracts/core/exchange-wrappers/ZeroExExchangeWrapper.sol @@ -75,14 +75,16 @@ contract ZeroExExchangeWrapper is * * TODO: We are currently assuming no taker fee. Add in taker fee going forward * - * @param _ Unused address of fillOrder caller to conform to IExchangeWrapper - * @param _orderCount Amount of orders in exchange request - * @param _ordersData Byte string containing (multiple) 0x wrapper orders - * @return address[] Array of token addresses executed in orders - * @return uint256[] Array of token amounts executed in orders + * @param _maker Unused address of issuance order signer to conform to IExchangeWrapper + * @param _taker Unused address of fillOrder caller to conform to IExchangeWrapper + * @param _orderCount Amount of orders in exchange request + * @param _ordersData Byte string containing (multiple) 0x wrapper orders + * @return address[] Array of token addresses executed in orders + * @return uint256[] Array of token amounts executed in orders */ function exchange( - address _, + address _maker, + address _taker, uint256 _orderCount, bytes _ordersData ) @@ -139,10 +141,11 @@ contract ZeroExExchangeWrapper is /** * Executes 0x order from signed order data * - * @param _order 0x order struct - * @param _signature Signature for order - * @param _header Struct containing wrapper order header data for order - * @return TakerFillResults 0x fill order structs + * @param _order 0x order struct + * @param _signature Signature for order + * @param _header Struct containing wrapper order header data for order + * @return address Address of set component (0x makerToken) in 0x order + * @return uint256 Amount of 0x order makerTokenAmount received */ function fillZeroExOrder( ZeroExOrder.Order memory _order, diff --git a/contracts/core/extensions/CoreIssuanceOrder.sol b/contracts/core/extensions/CoreIssuanceOrder.sol index 8638ac37c..0e5c7f4a1 100644 --- a/contracts/core/extensions/CoreIssuanceOrder.sol +++ b/contracts/core/extensions/CoreIssuanceOrder.sol @@ -288,6 +288,7 @@ contract CoreIssuanceOrder is address[] memory componentFillTokens = new address[](header.orderCount); uint256[] memory componentFillAmounts = new uint256[](header.orderCount); (componentFillTokens, componentFillAmounts) = IExchangeWrapper(exchange).exchange( + _makerAddress, msg.sender, header.orderCount, bodyData diff --git a/contracts/core/interfaces/IExchangeWrapper.sol b/contracts/core/interfaces/IExchangeWrapper.sol index 7108f1581..9731bf167 100644 --- a/contracts/core/interfaces/IExchangeWrapper.sol +++ b/contracts/core/interfaces/IExchangeWrapper.sol @@ -30,12 +30,14 @@ interface IExchangeWrapper { /** * Exchange some amount of makerToken for takerToken. * + * @param _maker Issuance order maker * @param _taker Issuance order taker * @param _orderCount Expected number of orders to execute * @param _orderData Arbitrary bytes data for any information to pass to the exchange * @return address[], uint256[] The taker token addresses and the associated quantities */ function exchange( + address _maker, address _taker, uint256 _orderCount, bytes _orderData diff --git a/contracts/external/KyberNetwork/KyberNetworkProxyInterface.sol b/contracts/external/KyberNetwork/KyberNetworkProxyInterface.sol new file mode 100644 index 000000000..46d535d42 --- /dev/null +++ b/contracts/external/KyberNetwork/KyberNetworkProxyInterface.sol @@ -0,0 +1,18 @@ +pragma solidity 0.4.24; + + +/// @title Kyber Network interface +interface KyberNetworkProxyInterface { + function trade( + address src, + uint srcAmount, + address dest, + address destAddress, + uint maxDestAmount, + uint minConversionRate, + address walletId + ) + public + payable + returns(uint); +} diff --git a/package.json b/package.json index 2374220d7..03058da27 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,7 @@ "husky": "^0.14.3", "lint-staged": "^7.2.0", "module-alias": "^2.1.0", - "set-protocol-utils": "^0.3.30", + "set-protocol-utils": "^0.3.32", "sol-trace-set": "^0.0.1", "solium": "^1.1.7", "tiny-promisify": "^1.0.0", diff --git a/test/core/exchange-wrappers/kyberNetworkWrapper.spec.ts b/test/core/exchange-wrappers/kyberNetworkWrapper.spec.ts new file mode 100644 index 000000000..641c43cf8 --- /dev/null +++ b/test/core/exchange-wrappers/kyberNetworkWrapper.spec.ts @@ -0,0 +1,200 @@ +require('module-alias/register'); + +import * as chai from 'chai'; +import * as ethUtil from 'ethereumjs-util'; +import * as setProtocolUtils from 'set-protocol-utils'; +import { BigNumber } from 'bignumber.js'; +import { Address, Bytes, KyberTrade } from 'set-protocol-utils'; + +import ChaiSetup from '@utils/chaiSetup'; +import { BigNumberSetup } from '@utils/bigNumberSetup'; +import { + StandardTokenMockContract, + KyberNetworkWrapperContract, + TransferProxyContract +} from '@utils/contracts'; +import { CoreWrapper } from '@utils/coreWrapper'; +import { ERC20Wrapper } from '@utils/erc20Wrapper'; +import { ExchangeWrapper } from '@utils/exchangeWrapper'; +import { Blockchain } from '@utils/blockchain'; +import { DEFAULT_GAS, UNLIMITED_ALLOWANCE_IN_BASE_UNITS } from '@utils/constants'; +import { expectRevertError } from '@utils/tokenAssertions'; + +BigNumberSetup.configure(); +ChaiSetup.configure(); +const { expect } = chai; +const { SetProtocolTestUtils: SetTestUtils, SetProtocolUtils: SetUtils } = setProtocolUtils; +const setUtils = new SetUtils(web3); +const blockchain = new Blockchain(web3); + + +contract('KyberNetworkWrapper', accounts => { + const [ + deployerAccount, + authorizedAddress, + unauthorizedAddress, + issuanceOrderMakerAccount, + takerAccount, + ] = accounts; + + const coreWrapper = new CoreWrapper(deployerAccount, deployerAccount); + const erc20Wrapper = new ERC20Wrapper(deployerAccount); + const exchangeWrapper = new ExchangeWrapper(deployerAccount); + + let transferProxy: TransferProxyContract; + let kyberNetworkWrapper: KyberNetworkWrapperContract; + + beforeEach(async () => { + await blockchain.saveSnapshotAsync(); + + transferProxy = await coreWrapper.deployTransferProxyAsync(); + + kyberNetworkWrapper = await exchangeWrapper.deployKyberNetworkWrapper( + SetTestUtils.KYBER_NETWORK_PROXY_ADDRESS, + transferProxy + ); + await coreWrapper.addAuthorizationAsync(kyberNetworkWrapper, authorizedAddress); + }); + + afterEach(async () => { + await blockchain.revertAsync(); + }); + + describe('#exchange', async () => { + let subjectCaller: Address; + let subjectMaker: Address; + let subjectTaker: Address; + let subjectTradesCount: BigNumber; + let subjectTradesData: Bytes; + + let maxDestinationQuantity: BigNumber; + + let sourceToken: StandardTokenMockContract; + let sourceTokenDecimals: number; + let sourceTokenQuantity: BigNumber; + + let componentToken: StandardTokenMockContract; + let componentTokenDecimals: number; + let componentTokenAmountToReceive: BigNumber; + + beforeEach(async () => { + componentToken = erc20Wrapper.kyberReserveToken(SetTestUtils.KYBER_RESERVE_DESTINATION_TOKEN_ADDRESS); + maxDestinationQuantity = componentTokenAmountToReceive || new BigNumber(93); + + sourceToken = erc20Wrapper.kyberReserveToken(SetTestUtils.KYBER_RESERVE_SOURCE_TOKEN_ADDRESS); + sourceTokenQuantity = new BigNumber(1450); + + await erc20Wrapper.transferTokenAsync( + sourceToken, + kyberNetworkWrapper.address, + sourceTokenQuantity, + issuanceOrderMakerAccount + ); + + componentTokenDecimals = (await componentToken.decimals.callAsync()).toNumber(); + sourceTokenDecimals = (await sourceToken.decimals.callAsync()).toNumber(); + const conversionRatePower = new BigNumber(10).pow(18 + sourceTokenDecimals - componentTokenDecimals); + const minimumConversionRate = maxDestinationQuantity.div(sourceTokenQuantity).mul(conversionRatePower).round(); + + const kyberTrade = { + sourceToken: sourceToken.address, + destinationToken: componentToken.address, + sourceTokenQuantity: sourceTokenQuantity, + minimumConversionRate: minimumConversionRate, + maxDestinationQuantity: maxDestinationQuantity, + } as KyberTrade; + + subjectCaller = authorizedAddress; + subjectMaker = issuanceOrderMakerAccount; + subjectTaker = takerAccount; + subjectTradesCount = new BigNumber(1); + subjectTradesData = ethUtil.bufferToHex(setUtils.kyberTradeToBuffer(kyberTrade)); + }); + + async function subject(): Promise { + return kyberNetworkWrapper.exchange.sendTransactionAsync( + subjectMaker, + subjectTaker, + subjectTradesCount, + subjectTradesData, + { from: subjectCaller, gas: DEFAULT_GAS }, + ); + } + + it('uses all of the available source token from the kyber wrapper', async () => { + const existingSourceTokenBalance = await sourceToken.balanceOf.callAsync(kyberNetworkWrapper.address); + + await subject(); + + const expectedNewBalance = existingSourceTokenBalance.sub(sourceTokenQuantity); + const newSourceTokenBalance = await sourceToken.balanceOf.callAsync(kyberNetworkWrapper.address); + expect(newSourceTokenBalance).to.be.bignumber.equal(expectedNewBalance); + }); + + it('receives correct amount of component token in return', async () => { + const existingComponentToken = await componentToken.balanceOf.callAsync(kyberNetworkWrapper.address); + + await subject(); + + const expectedNewBalance = existingComponentToken.add(maxDestinationQuantity); + const newComponentTokenBalance = await componentToken.balanceOf.callAsync(kyberNetworkWrapper.address); + expect(newComponentTokenBalance).to.be.bignumber.equal(expectedNewBalance); + }); + + it('approves the token for transfer to the transferProxy with unlimited allowance', async () => { + const existingAllowance = + await componentToken.allowance.callAsync(kyberNetworkWrapper.address, transferProxy.address); + + await subject(); + + const expectedNewAllowance = existingAllowance.add(UNLIMITED_ALLOWANCE_IN_BASE_UNITS); + const newBalance = + await componentToken.allowance.callAsync(kyberNetworkWrapper.address, transferProxy.address); + expect(newBalance).to.be.bignumber.equal(expectedNewAllowance); + }); + + describe('when the caller is not authorized', async () => { + beforeEach(async () => { + subjectCaller = unauthorizedAddress; + }); + + it('should revert', async () => { + await expectRevertError(subject()); + }); + }); + + describe('when there is remainder source token as a result of the trade parameters', async () => { + before(async () => { + componentTokenAmountToReceive = new BigNumber(50); + }); + + after(async () => { + componentTokenAmountToReceive = undefined; + }); + + it('only trades up to the specified amount to receive', async () => { + const existingComponentToken = await componentToken.balanceOf.callAsync(kyberNetworkWrapper.address); + + await subject(); + + const expectedNewBalance = existingComponentToken.add(componentTokenAmountToReceive); + const newComponentTokenBalance = await componentToken.balanceOf.callAsync(kyberNetworkWrapper.address); + expect(newComponentTokenBalance).to.be.bignumber.equal(expectedNewBalance); + }); + + it('transfers the remainder back to the maker', async () => { + const existingUserSourceTokenBalance = await sourceToken.balanceOf.callAsync(issuanceOrderMakerAccount); + + await subject(); + + const newUserSourceTokenBalance = await sourceToken.balanceOf.callAsync(issuanceOrderMakerAccount); + const receivedComponentTokenAmount = await componentToken.balanceOf.callAsync(kyberNetworkWrapper.address); + + const sourceTokenUsed = sourceTokenQuantity.sub(newUserSourceTokenBalance.sub(existingUserSourceTokenBalance)); + const conversionRate = receivedComponentTokenAmount.div(sourceTokenUsed); + const expectedTokenAmountToReceive = sourceTokenUsed.mul(conversionRate).round(); + expect(componentTokenAmountToReceive).to.be.bignumber.equal(expectedTokenAmountToReceive); + }); + }); + }); +}); diff --git a/test/core/exchange-wrappers/takerWalletWrapper.spec.ts b/test/core/exchange-wrappers/takerWalletWrapper.spec.ts index ac08424cc..43f29da97 100644 --- a/test/core/exchange-wrappers/takerWalletWrapper.spec.ts +++ b/test/core/exchange-wrappers/takerWalletWrapper.spec.ts @@ -33,6 +33,7 @@ const blockchain = new Blockchain(web3); contract('TakerWalletWrapper', accounts => { const [ deployerAccount, + makerAccount, takerAccount, authorizedAddress, unauthorizedAddress, @@ -86,6 +87,7 @@ contract('TakerWalletWrapper', accounts => { async function subject(): Promise { return takerWalletWrapper.exchange.sendTransactionAsync( + makerAccount, takerAccount, subjectOrderCount, subjectTakerOrdersData, diff --git a/test/core/exchange-wrappers/zeroExExchangeWrapper.spec.ts b/test/core/exchange-wrappers/zeroExExchangeWrapper.spec.ts index 02c31fa72..ad3c47d0c 100644 --- a/test/core/exchange-wrappers/zeroExExchangeWrapper.spec.ts +++ b/test/core/exchange-wrappers/zeroExExchangeWrapper.spec.ts @@ -39,6 +39,7 @@ contract('ZeroExExchangeWrapper', accounts => { zrxTokenOwnerAccount, deployerAccount, zeroExOrderMakerAccount, + issuanceOrderMakerAccount, issuanceOrderAndZeroExOrderTakerAccount, secondZeroExOrderMakerAccount, ] = accounts; @@ -66,7 +67,7 @@ contract('ZeroExExchangeWrapper', accounts => { ); await coreWrapper.addAuthorizationAsync(zeroExExchangeWrapper, deployerAccount); - zrxToken = await erc20Wrapper.zrxToken(zrxTokenOwnerAccount); + zrxToken = erc20Wrapper.zrxToken(); const orderTakerZRXBalanceForFees = ether(1000); await erc20Wrapper.transferTokenAsync( zrxToken, @@ -90,6 +91,7 @@ contract('ZeroExExchangeWrapper', accounts => { }); describe('#exchange', async () => { + let subjectMakerAccount: Address; let subjectTakerAccount: Address; let subjectOrderCount: BigNumber; let subjectOrderData: Bytes; @@ -109,9 +111,6 @@ contract('ZeroExExchangeWrapper', accounts => { let zeroExExchangeWrapperOrder: Bytes; beforeEach(async () => { - subjectTakerAccount = issuanceOrderAndZeroExOrderTakerAccount; - subjectOrderCount = new BigNumber(1); - senderAddress = senderAddress || NULL_ADDRESS; makerAddress = makerAddress || zeroExOrderMakerAccount; takerAddress = takerAddress || NULL_ADDRESS; @@ -146,11 +145,16 @@ contract('ZeroExExchangeWrapper', accounts => { zeroExOrderSignature, zeroExOrderFillAmount ); + + subjectMakerAccount = issuanceOrderMakerAccount; + subjectTakerAccount = issuanceOrderAndZeroExOrderTakerAccount; + subjectOrderCount = new BigNumber(1); subjectOrderData = zeroExExchangeWrapperOrder; }); async function subject(): Promise { return zeroExExchangeWrapper.exchange.sendTransactionAsync( + subjectMakerAccount, subjectTakerAccount, subjectOrderCount, subjectOrderData @@ -315,6 +319,7 @@ contract('ZeroExExchangeWrapper', accounts => { context('when checking the return value', async () => { async function subject(): Promise { return zeroExExchangeWrapper.exchange.callAsync( + subjectMakerAccount, subjectTakerAccount, subjectOrderCount, subjectOrderData, diff --git a/utils/contracts.ts b/utils/contracts.ts index 1e675809e..8a38a20c2 100644 --- a/utils/contracts.ts +++ b/utils/contracts.ts @@ -9,6 +9,7 @@ import { CoreMockContract } from '../types/generated/core_mock'; import { DetailedERC20Contract } from '../types/generated/detailed_erc20'; import { ERC20WrapperMockContract } from '../types/generated/erc20_wrapper_mock'; import { InvalidReturnTokenMockContract } from '../types/generated/invalid_return_token_mock'; +import { KyberNetworkWrapperContract } from '../types/generated/kyber_network_wrapper'; import { NoDecimalTokenMockContract } from '../types/generated/no_decimal_token_mock'; import { NoXferReturnTokenMockContract } from '../types/generated/no_xfer_return_token_mock'; import { OrderLibraryMockContract } from '../types/generated/order_library_mock'; @@ -35,6 +36,7 @@ export { DetailedERC20Contract, ERC20WrapperMockContract, InvalidReturnTokenMockContract, + KyberNetworkWrapperContract, NoDecimalTokenMockContract, NoXferReturnTokenMockContract, OrderLibraryMockContract, diff --git a/utils/erc20Wrapper.ts b/utils/erc20Wrapper.ts index 9e076fc3c..dd2536611 100644 --- a/utils/erc20Wrapper.ts +++ b/utils/erc20Wrapper.ts @@ -172,20 +172,16 @@ export class ERC20Wrapper { ); } - public async zrxToken( - initialAccount: Address - ): Promise { - const truffleMockToken = await StandardTokenMock.new( - initialAccount, - DEPLOYED_TOKEN_QUANTITY, - 'Zero Ex Mock', - 'ZRX', - DEFAULT_MOCK_TOKEN_DECIMALS, - { from: this._senderAccountAddress, gas: DEFAULT_GAS }, + public zrxToken(): StandardTokenMockContract { + return new StandardTokenMockContract( + web3.eth.contract(StandardTokenMock.abi).at(TestUtils.ZERO_EX_TOKEN_ADDRESS), + { from: this._senderAccountAddress }, ); + } + public kyberReserveToken(tokenAddress: Address): StandardTokenMockContract { return new StandardTokenMockContract( - web3.eth.contract(truffleMockToken.abi).at(TestUtils.ZERO_EX_TOKEN_ADDRESS), + web3.eth.contract(StandardTokenMock.abi).at(tokenAddress), { from: this._senderAccountAddress }, ); } diff --git a/utils/exchangeWrapper.ts b/utils/exchangeWrapper.ts index e3a5923be..94ae18bfd 100644 --- a/utils/exchangeWrapper.ts +++ b/utils/exchangeWrapper.ts @@ -3,6 +3,7 @@ import { Address } from 'set-protocol-utils'; import { CoreContract, + KyberNetworkWrapperContract, TakerWalletWrapperContract, TransferProxyContract, ZeroExExchangeWrapperContract, @@ -12,6 +13,7 @@ import { CoreWrapper } from './coreWrapper'; import { DEFAULT_GAS } from './constants'; const ERC20Wrapper = artifacts.require('ERC20Wrapper'); +const KyberNetworkWrapper = artifacts.require('KyberNetworkWrapper'); const TakerWalletWrapper = artifacts.require('TakerWalletWrapper'); const ZeroExExchangeWrapper = artifacts.require('ZeroExExchangeWrapper'); const { SetProtocolUtils: SetUtils } = setProtocolUtils; @@ -28,6 +30,28 @@ export class ExchangeWrapper { /* ============ Deployment ============ */ + public async deployKyberNetworkWrapper( + kyberNetworkProxy: Address, + transferProxy: TransferProxyContract, + from: Address = this._contractOwnerAddress + ): Promise { + const truffleERC20Wrapper = await ERC20Wrapper.new( + { from }, + ); + + await KyberNetworkWrapper.link('ERC20Wrapper', truffleERC20Wrapper.address); + const kyberNetworkWrapperInstance = await KyberNetworkWrapper.new( + kyberNetworkProxy, + transferProxy.address, + { from, gas: DEFAULT_GAS }, + ); + + return new KyberNetworkWrapperContract( + web3.eth.contract(kyberNetworkWrapperInstance.abi).at(kyberNetworkWrapperInstance.address), + { from }, + ); + } + public async deployTakerWalletExchangeWrapper( transferProxy: TransferProxyContract, from: Address = this._contractOwnerAddress diff --git a/yarn.lock b/yarn.lock index e99ad1ce8..0ca65cf94 100644 --- a/yarn.lock +++ b/yarn.lock @@ -29,13 +29,13 @@ lodash "^4.17.5" valid-url "^1.0.9" -"@0xproject/assert@^1.0.8": - version "1.0.8" - resolved "https://registry.yarnpkg.com/@0xproject/assert/-/assert-1.0.8.tgz#458580519eb8d0c5fb8bef3da96fcd0820639d05" +"@0xproject/assert@^1.0.9": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@0xproject/assert/-/assert-1.0.9.tgz#01676bf49b6c8702dab421a36fb1afa93718f31b" dependencies: - "@0xproject/json-schemas" "^1.0.1" - "@0xproject/typescript-typings" "^2.0.0" - "@0xproject/utils" "^1.0.8" + "@0xproject/json-schemas" "^1.0.2" + "@0xproject/typescript-typings" "^2.0.1" + "@0xproject/utils" "^1.0.9" lodash "^4.17.5" valid-url "^1.0.9" @@ -61,26 +61,17 @@ ethers "3.0.22" lodash "^4.17.5" -"@0xproject/base-contract@^2.0.2": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@0xproject/base-contract/-/base-contract-2.0.2.tgz#e73eb44d4f7d39a48fddf44e853fb7a4430782ac" +"@0xproject/base-contract@^2.0.3": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@0xproject/base-contract/-/base-contract-2.0.3.tgz#37c789730ae3b0fb4a460bd8816a1555f70730fe" dependencies: - "@0xproject/typescript-typings" "^2.0.0" - "@0xproject/utils" "^1.0.8" - "@0xproject/web3-wrapper" "^2.0.2" - ethereum-types "^1.0.6" + "@0xproject/typescript-typings" "^2.0.1" + "@0xproject/utils" "^1.0.9" + "@0xproject/web3-wrapper" "^2.0.3" + ethereum-types "^1.0.7" ethers "3.0.22" lodash "^4.17.5" -"@0xproject/json-schemas@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@0xproject/json-schemas/-/json-schemas-1.0.1.tgz#f28ef71872f8ad63e8ca871ce8d159a92cf24647" - dependencies: - "@0xproject/typescript-typings" "^2.0.0" - "@types/node" "^8.0.53" - jsonschema "^1.2.0" - lodash.values "^4.3.0" - "@0xproject/json-schemas@^1.0.1-rc.4", "@0xproject/json-schemas@^1.0.1-rc.5": version "1.0.1-rc.5" resolved "https://registry.yarnpkg.com/@0xproject/json-schemas/-/json-schemas-1.0.1-rc.5.tgz#4ae2e13ee4ae8b19e9432c2ef40d6d3ff8ebdbb9" @@ -90,20 +81,29 @@ jsonschema "^1.2.0" lodash.values "^4.3.0" -"@0xproject/order-utils@^1.0.1": +"@0xproject/json-schemas@^1.0.2": version "1.0.2" - resolved "https://registry.yarnpkg.com/@0xproject/order-utils/-/order-utils-1.0.2.tgz#c7d60829df0f5a8a879bfe0254f2921e369017fe" - dependencies: - "@0xproject/assert" "^1.0.8" - "@0xproject/base-contract" "^2.0.2" - "@0xproject/json-schemas" "^1.0.1" - "@0xproject/types" "^1.0.1" - "@0xproject/typescript-typings" "^2.0.0" - "@0xproject/utils" "^1.0.8" - "@0xproject/web3-wrapper" "^2.0.2" - "@types/node" "^8.0.53" + resolved "https://registry.yarnpkg.com/@0xproject/json-schemas/-/json-schemas-1.0.2.tgz#baabcf8dd8286ddb11204d9b590e0c2e3a26deea" + dependencies: + "@0xproject/typescript-typings" "^2.0.1" + "@types/node" "*" + jsonschema "^1.2.0" + lodash.values "^4.3.0" + +"@0xproject/order-utils@^1.0.1-rc.2": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@0xproject/order-utils/-/order-utils-1.0.3.tgz#7a9c32bcc96cd244456093bac59fa05587f1fa26" + dependencies: + "@0xproject/assert" "^1.0.9" + "@0xproject/base-contract" "^2.0.3" + "@0xproject/json-schemas" "^1.0.2" + "@0xproject/types" "^1.0.2" + "@0xproject/typescript-typings" "^2.0.1" + "@0xproject/utils" "^1.0.9" + "@0xproject/web3-wrapper" "^2.0.3" + "@types/node" "*" bn.js "^4.11.8" - ethereum-types "^1.0.6" + ethereum-types "^1.0.7" ethereumjs-abi "0.6.5" ethereumjs-util "^5.1.1" ethers "3.0.22" @@ -128,14 +128,6 @@ ethers "3.0.22" lodash "^4.17.5" -"@0xproject/types@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@0xproject/types/-/types-1.0.1.tgz#a26493db54971c41342d8d7f1bebc3e5fcb74ccf" - dependencies: - "@types/node" "^8.0.53" - bignumber.js "~4.1.0" - ethereum-types "^1.0.6" - "@0xproject/types@^1.0.1-rc.3", "@0xproject/types@^1.0.1-rc.4", "@0xproject/types@^1.0.1-rc.5": version "1.0.1-rc.5" resolved "https://registry.yarnpkg.com/@0xproject/types/-/types-1.0.1-rc.5.tgz#4c403a2fc8795e45034bda2b0e588a77c2767ea2" @@ -144,6 +136,14 @@ bignumber.js "~4.1.0" ethereum-types "^1.0.5" +"@0xproject/types@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@0xproject/types/-/types-1.0.2.tgz#76a7106f6c19b0ed9cde7dc1218dcc4252d3b397" + dependencies: + "@types/node" "*" + bignumber.js "~4.1.0" + ethereum-types "^1.0.7" + "@0xproject/typescript-typings@^1.0.3", "@0xproject/typescript-typings@^1.0.4", "@0xproject/typescript-typings@^1.0.5": version "1.0.5" resolved "https://registry.yarnpkg.com/@0xproject/typescript-typings/-/typescript-typings-1.0.5.tgz#a808443419f26a7b90d63d1afd3efbfb48644184" @@ -154,14 +154,14 @@ ethereum-types "^1.0.5" popper.js "1.14.3" -"@0xproject/typescript-typings@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@0xproject/typescript-typings/-/typescript-typings-2.0.0.tgz#bd514c5edef304456224473aeee8dacd5bb0b99f" +"@0xproject/typescript-typings@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@0xproject/typescript-typings/-/typescript-typings-2.0.1.tgz#328e80d650092a2e1a7b156796a401811b066df6" dependencies: "@types/bn.js" "^4.11.0" "@types/react" "*" bignumber.js "~4.1.0" - ethereum-types "^1.0.6" + ethereum-types "^1.0.7" popper.js "1.14.3" "@0xproject/utils@^1.0.4", "@0xproject/utils@^1.0.5", "@0xproject/utils@^1.0.6": @@ -181,17 +181,17 @@ js-sha3 "^0.7.0" lodash "^4.17.5" -"@0xproject/utils@^1.0.8": - version "1.0.8" - resolved "https://registry.yarnpkg.com/@0xproject/utils/-/utils-1.0.8.tgz#4d3f169da0d769025090e22bdbff18d795b6fe1d" +"@0xproject/utils@^1.0.9": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@0xproject/utils/-/utils-1.0.9.tgz#6154f69d5618f4675e9429879b59e6162ee5d985" dependencies: - "@0xproject/types" "^1.0.1" - "@0xproject/typescript-typings" "^2.0.0" - "@types/node" "^8.0.53" + "@0xproject/types" "^1.0.2" + "@0xproject/typescript-typings" "^2.0.1" + "@types/node" "*" abortcontroller-polyfill "^1.1.9" bignumber.js "~4.1.0" detect-node "2.0.3" - ethereum-types "^1.0.6" + ethereum-types "^1.0.7" ethereumjs-util "^5.1.1" ethers "3.0.22" isomorphic-fetch "^2.2.1" @@ -224,15 +224,15 @@ ethers "3.0.22" lodash "^4.17.5" -"@0xproject/web3-wrapper@^2.0.2": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@0xproject/web3-wrapper/-/web3-wrapper-2.0.2.tgz#3f8331e19da383318b4cbf801fc94035e6952788" +"@0xproject/web3-wrapper@^2.0.3": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@0xproject/web3-wrapper/-/web3-wrapper-2.0.3.tgz#fe5e9950f36261ea3c6c798639376f0bcef092d0" dependencies: - "@0xproject/assert" "^1.0.8" - "@0xproject/json-schemas" "^1.0.1" - "@0xproject/typescript-typings" "^2.0.0" - "@0xproject/utils" "^1.0.8" - ethereum-types "^1.0.6" + "@0xproject/assert" "^1.0.9" + "@0xproject/json-schemas" "^1.0.2" + "@0xproject/typescript-typings" "^2.0.1" + "@0xproject/utils" "^1.0.9" + ethereum-types "^1.0.7" ethereumjs-util "^5.1.1" ethers "3.0.22" lodash "^4.17.5" @@ -2041,11 +2041,11 @@ ethereum-types@^1.0.0, ethereum-types@^1.0.3, ethereum-types@^1.0.4, ethereum-ty "@types/node" "^8.0.53" bignumber.js "~4.1.0" -ethereum-types@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/ethereum-types/-/ethereum-types-1.0.6.tgz#9e8fd73895e9f0a4941b384a3d0c8244dedc2a64" +ethereum-types@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/ethereum-types/-/ethereum-types-1.0.7.tgz#880833ee3dbbf37e9b00cc5c7a635ed91d063992" dependencies: - "@types/node" "^8.0.53" + "@types/node" "*" bignumber.js "~4.1.0" ethereumjs-abi@0.6.5, ethereumjs-abi@^0.6.4: @@ -4933,12 +4933,12 @@ set-immediate-shim@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" -set-protocol-utils@^0.3.30: - version "0.3.30" - resolved "https://registry.yarnpkg.com/set-protocol-utils/-/set-protocol-utils-0.3.30.tgz#d24f5cd34aa76b912a0744b958da47c976631b83" +set-protocol-utils@^0.3.32: + version "0.3.32" + resolved "https://registry.yarnpkg.com/set-protocol-utils/-/set-protocol-utils-0.3.32.tgz#91150dd15d0ec2bc51cfc305d248d1d52db7d588" dependencies: "@0xproject/base-contract" "^1.0.4" - "@0xproject/order-utils" "^1.0.1" + "@0xproject/order-utils" "^1.0.1-rc.2" "@0xproject/types" "^1.0.1-rc.3" "@0xproject/typescript-typings" "^1.0.4" "@0xproject/web3-wrapper" "^1.1.2"