Skip to content
This repository was archived by the owner on Jan 18, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
220 changes: 220 additions & 0 deletions contracts/core/exchange-wrappers/KyberNetworkWrapper.sol
Original file line number Diff line number Diff line change
@@ -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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the trade data size is fixed, can we make this a variable defined on the top of the file vs. a magic number?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Normally, yes. In solidity, no since it’s another reference which adds gas

}

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(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OPTIMIZATION: Performing a transfer each time seems costly. Is there a way we can accumulate the remainderSourceToken and send it once after all Kyber trades have been executed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

_trade.sourceToken,
_maker,
remainderSourceToken
);
}

// Ensure the maker token is allowed to be transferred by Set TransferProxy
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Knit: Space between Transfer and Proxy

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaving it in, meant to refer to the contract name

ERC20.ensureAllowance(
_trade.destinationToken,
address(this),
setTransferProxy,
destinationTokenQuantity
);

return destinationTokenQuantity;
}

/*
* Parses the bytes array for a Kyber trade
*
* | Data | Location |
* |----------------------------|-------------------------------|
* | sourceToken | 0 |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems useful to add the data type as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just needs to show which parameter is at which index, the types are revealed in the struct at the top of this file

* | 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;
}
}
12 changes: 7 additions & 5 deletions contracts/core/exchange-wrappers/TakerWalletWrapper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
23 changes: 13 additions & 10 deletions contracts/core/exchange-wrappers/ZeroExExchangeWrapper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
Expand Down Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions contracts/core/extensions/CoreIssuanceOrder.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions contracts/core/interfaces/IExchangeWrapper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 18 additions & 0 deletions contracts/external/KyberNetwork/KyberNetworkProxyInterface.sol
Original file line number Diff line number Diff line change
@@ -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);
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Loading