Compare commits

...

5 Commits

Author SHA1 Message Date
Phil Liao
2baaa2b2db wip feat: add support for RFQt v2 in asset swapper 2022-07-06 17:23:34 -05:00
Noah Khamliche
71292a14ae uncomment the marketOperationUtils tests 2022-06-15 18:37:19 -04:00
Noah Khamliche
46b3d5b1ab reverted chagnge to otc default values 2022-06-15 18:33:26 -04:00
Noah Khamliche
b4cf5d5711 added new signedOtcOrder type 2022-06-15 18:31:43 -04:00
Noah Khamliche
f69b19faca added otc orders to the fqt, and protocol-utils. starting epSwapQuoteConsumer fill logic 2022-06-01 18:14:24 -04:00
17 changed files with 547 additions and 233 deletions

View File

@@ -20,22 +20,17 @@
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "../libs/LibSignature.sol";
import "../libs/LibNativeOrder.sol";
import "./INativeOrdersEvents.sol";
import '@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol';
import '../libs/LibSignature.sol';
import '../libs/LibNativeOrder.sol';
import './INativeOrdersEvents.sol';
/// @dev Feature for interacting with limit orders.
interface INativeOrdersFeature is
INativeOrdersEvents
{
interface INativeOrdersFeature is INativeOrdersEvents {
/// @dev Transfers protocol fees from the `FeeCollector` pools into
/// the staking contract.
/// @param poolIds Staking pool IDs
function transferProtocolFeesForPools(bytes32[] calldata poolIds)
external;
function transferProtocolFeesForPools(bytes32[] calldata poolIds) external;
/// @dev Fill a limit order. The taker and sender will be the caller.
/// @param order The limit order. ETH protocol fees can be
@@ -49,10 +44,7 @@ interface INativeOrdersFeature is
LibNativeOrder.LimitOrder calldata order,
LibSignature.Signature calldata signature,
uint128 takerTokenFillAmount
)
external
payable
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount);
) external payable returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount);
/// @dev Fill an RFQ order for up to `takerTokenFillAmount` taker tokens.
/// The taker will be the caller.
@@ -65,9 +57,7 @@ interface INativeOrdersFeature is
LibNativeOrder.RfqOrder calldata order,
LibSignature.Signature calldata signature,
uint128 takerTokenFillAmount
)
external
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount);
) external returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount);
/// @dev Fill an RFQ order for exactly `takerTokenFillAmount` taker tokens.
/// The taker will be the caller. ETH protocol fees can be
@@ -81,10 +71,7 @@ interface INativeOrdersFeature is
LibNativeOrder.LimitOrder calldata order,
LibSignature.Signature calldata signature,
uint128 takerTokenFillAmount
)
external
payable
returns (uint128 makerTokenFilledAmount);
) external payable returns (uint128 makerTokenFilledAmount);
/// @dev Fill an RFQ order for exactly `takerTokenFillAmount` taker tokens.
/// The taker will be the caller.
@@ -96,9 +83,7 @@ interface INativeOrdersFeature is
LibNativeOrder.RfqOrder calldata order,
LibSignature.Signature calldata signature,
uint128 takerTokenFillAmount
)
external
returns (uint128 makerTokenFilledAmount);
) external returns (uint128 makerTokenFilledAmount);
/// @dev Fill a limit order. Internal variant. ETH protocol fees can be
/// attached to this call. Any unspent ETH will be refunded to
@@ -116,10 +101,7 @@ interface INativeOrdersFeature is
uint128 takerTokenFillAmount,
address taker,
address sender
)
external
payable
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount);
) external payable returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount);
/// @dev Fill an RFQ order. Internal variant.
/// @param order The RFQ order.
@@ -138,40 +120,33 @@ interface INativeOrdersFeature is
address taker,
bool useSelfBalance,
address recipient
)
external
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount);
) external returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount);
/// @dev Cancel a single limit order. The caller must be the maker or a valid order signer.
/// Silently succeeds if the order has already been cancelled.
/// @param order The limit order.
function cancelLimitOrder(LibNativeOrder.LimitOrder calldata order)
external;
function cancelLimitOrder(LibNativeOrder.LimitOrder calldata order) external;
/// @dev Cancel a single RFQ order. The caller must be the maker or a valid order signer.
/// Silently succeeds if the order has already been cancelled.
/// @param order The RFQ order.
function cancelRfqOrder(LibNativeOrder.RfqOrder calldata order)
external;
function cancelRfqOrder(LibNativeOrder.RfqOrder calldata order) external;
/// @dev Mark what tx.origin addresses are allowed to fill an order that
/// specifies the message sender as its txOrigin.
/// @param origins An array of origin addresses to update.
/// @param allowed True to register, false to unregister.
function registerAllowedRfqOrigins(address[] memory origins, bool allowed)
external;
function registerAllowedRfqOrigins(address[] memory origins, bool allowed) external;
/// @dev Cancel multiple limit orders. The caller must be the maker or a valid order signer.
/// Silently succeeds if the order has already been cancelled.
/// @param orders The limit orders.
function batchCancelLimitOrders(LibNativeOrder.LimitOrder[] calldata orders)
external;
function batchCancelLimitOrders(LibNativeOrder.LimitOrder[] calldata orders) external;
/// @dev Cancel multiple RFQ orders. The caller must be the maker or a valid order signer.
/// Silently succeeds if the order has already been cancelled.
/// @param orders The RFQ orders.
function batchCancelRfqOrders(LibNativeOrder.RfqOrder[] calldata orders)
external;
function batchCancelRfqOrders(LibNativeOrder.RfqOrder[] calldata orders) external;
/// @dev Cancel all limit orders for a given maker and pair with a salt less
/// than the value provided. The caller must be the maker. Subsequent
@@ -184,8 +159,7 @@ interface INativeOrdersFeature is
IERC20TokenV06 makerToken,
IERC20TokenV06 takerToken,
uint256 minValidSalt
)
external;
) external;
/// @dev Cancel all limit orders for a given maker and pair with a salt less
/// than the value provided. The caller must be a signer registered to the maker.
@@ -200,8 +174,7 @@ interface INativeOrdersFeature is
IERC20TokenV06 makerToken,
IERC20TokenV06 takerToken,
uint256 minValidSalt
)
external;
) external;
/// @dev Cancel all limit orders for a given maker and pairs with salts less
/// than the values provided. The caller must be the maker. Subsequent
@@ -214,8 +187,7 @@ interface INativeOrdersFeature is
IERC20TokenV06[] calldata makerTokens,
IERC20TokenV06[] calldata takerTokens,
uint256[] calldata minValidSalts
)
external;
) external;
/// @dev Cancel all limit orders for a given maker and pairs with salts less
/// than the values provided. The caller must be a signer registered to the maker.
@@ -230,8 +202,7 @@ interface INativeOrdersFeature is
IERC20TokenV06[] memory makerTokens,
IERC20TokenV06[] memory takerTokens,
uint256[] memory minValidSalts
)
external;
) external;
/// @dev Cancel all RFQ orders for a given maker and pair with a salt less
/// than the value provided. The caller must be the maker. Subsequent
@@ -244,8 +215,7 @@ interface INativeOrdersFeature is
IERC20TokenV06 makerToken,
IERC20TokenV06 takerToken,
uint256 minValidSalt
)
external;
) external;
/// @dev Cancel all RFQ orders for a given maker and pair with a salt less
/// than the value provided. The caller must be a signer registered to the maker.
@@ -260,8 +230,7 @@ interface INativeOrdersFeature is
IERC20TokenV06 makerToken,
IERC20TokenV06 takerToken,
uint256 minValidSalt
)
external;
) external;
/// @dev Cancel all RFQ orders for a given maker and pairs with salts less
/// than the values provided. The caller must be the maker. Subsequent
@@ -274,8 +243,7 @@ interface INativeOrdersFeature is
IERC20TokenV06[] calldata makerTokens,
IERC20TokenV06[] calldata takerTokens,
uint256[] calldata minValidSalts
)
external;
) external;
/// @dev Cancel all RFQ orders for a given maker and pairs with salts less
/// than the values provided. The caller must be a signer registered to the maker.
@@ -290,8 +258,7 @@ interface INativeOrdersFeature is
IERC20TokenV06[] memory makerTokens,
IERC20TokenV06[] memory takerTokens,
uint256[] memory minValidSalts
)
external;
) external;
/// @dev Get the order info for a limit order.
/// @param order The limit order.
@@ -312,26 +279,17 @@ interface INativeOrdersFeature is
/// @dev Get the canonical hash of a limit order.
/// @param order The limit order.
/// @return orderHash The order hash.
function getLimitOrderHash(LibNativeOrder.LimitOrder calldata order)
external
view
returns (bytes32 orderHash);
function getLimitOrderHash(LibNativeOrder.LimitOrder calldata order) external view returns (bytes32 orderHash);
/// @dev Get the canonical hash of an RFQ order.
/// @param order The RFQ order.
/// @return orderHash The order hash.
function getRfqOrderHash(LibNativeOrder.RfqOrder calldata order)
external
view
returns (bytes32 orderHash);
function getRfqOrderHash(LibNativeOrder.RfqOrder calldata order) external view returns (bytes32 orderHash);
/// @dev Get the protocol fee multiplier. This should be multiplied by the
/// gas price to arrive at the required protocol fee to fill a native order.
/// @return multiplier The protocol fee multiplier.
function getProtocolFeeMultiplier()
external
view
returns (uint32 multiplier);
function getProtocolFeeMultiplier() external view returns (uint32 multiplier);
/// @dev Get order info, fillable amount, and signature validity for a limit order.
/// Fillable amount is determined using balances and allowances of the maker.
@@ -361,10 +319,7 @@ interface INativeOrdersFeature is
/// @return actualFillableTakerTokenAmount How much of the order is fillable
/// based on maker funds, in taker tokens.
/// @return isSignatureValid Whether the signature is valid.
function getRfqOrderRelevantState(
LibNativeOrder.RfqOrder calldata order,
LibSignature.Signature calldata signature
)
function getRfqOrderRelevantState(LibNativeOrder.RfqOrder calldata order, LibSignature.Signature calldata signature)
external
view
returns (
@@ -419,20 +374,10 @@ interface INativeOrdersFeature is
/// This allows one to sign on behalf of a contract that calls this function
/// @param signer The address from which you plan to generate signatures
/// @param allowed True to register, false to unregister.
function registerAllowedOrderSigner(
address signer,
bool allowed
)
external;
function registerAllowedOrderSigner(address signer, bool allowed) external;
/// @dev checks if a given address is registered to sign on behalf of a maker address
/// @param maker The maker address encoded in an order (can be a contract)
/// @param signer The address that is providing a signature
function isValidOrderSigner(
address maker,
address signer
)
external
view
returns (bool isAllowed);
function isValidOrderSigner(address maker, address signer) external view returns (bool isAllowed);
}

View File

@@ -20,23 +20,22 @@
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/IERC20TokenV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
import "@0x/contracts-utils/contracts/src/v06/LibMathV06.sol";
import "../errors/LibTransformERC20RichErrors.sol";
import "../features/interfaces/INativeOrdersFeature.sol";
import "../features/libs/LibNativeOrder.sol";
import "./bridges/IBridgeAdapter.sol";
import "./Transformer.sol";
import "./LibERC20Transformer.sol";
import '@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol';
import '@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol';
import '@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol';
import '@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol';
import '@0x/contracts-utils/contracts/src/v06/LibMathV06.sol';
import '../errors/LibTransformERC20RichErrors.sol';
import '../features/interfaces/INativeOrdersFeature.sol';
import '../features/libs/LibNativeOrder.sol';
import './bridges/IBridgeAdapter.sol';
import './Transformer.sol';
import './LibERC20Transformer.sol';
import '../IZeroEx.sol';
/// @dev A transformer that fills an ERC20 market sell/buy quote.
/// This transformer shortcuts bridge orders and fills them directly
contract FillQuoteTransformer is
Transformer
{
contract FillQuoteTransformer is Transformer {
using LibERC20TokenV06 for IERC20TokenV06;
using LibERC20Transformer for IERC20TokenV06;
using LibSafeMathV06 for uint256;
@@ -52,7 +51,8 @@ contract FillQuoteTransformer is
enum OrderType {
Bridge,
Limit,
Rfq
Rfq,
Otc
}
struct LimitOrderInfo {
@@ -69,6 +69,13 @@ contract FillQuoteTransformer is
uint256 maxTakerTokenFillAmount;
}
struct OtcOrderInfo {
LibNativeOrder.OtcOrder order;
LibSignature.Signature signature;
// Maximum taker token amount of this limit order to fill.
uint256 maxTakerTokenFillAmount;
}
/// @dev Transform data to ABI-encode and pass into `transform()`.
struct TransformData {
// Whether we are performing a market sell or buy.
@@ -79,26 +86,24 @@ contract FillQuoteTransformer is
// The token being bought.
// This should be an actual token, not the ETH pseudo-token.
IERC20TokenV06 buyToken;
// External liquidity bridge orders. Sorted by fill sequence.
IBridgeAdapter.BridgeOrder[] bridgeOrders;
// Native limit orders. Sorted by fill sequence.
LimitOrderInfo[] limitOrders;
// Native RFQ orders. Sorted by fill sequence.
RfqOrderInfo[] rfqOrders;
// Otc orders.
OtcOrderInfo[] otcOrders;
// The sequence to fill the orders in. Each item will fill the next
// order of that type in either `bridgeOrders`, `limitOrders`,
// or `rfqOrders.`
// `rfqOrders`, or `otcOrders.`
OrderType[] fillSequence;
// Amount of `sellToken` to sell or `buyToken` to buy.
// For sells, setting the high-bit indicates that
// `sellAmount & LOW_BITS` should be treated as a `1e18` fraction of
// the current balance of `sellToken`, where
// `1e18+ == 100%` and `0.5e18 == 50%`, etc.
uint256 fillAmount;
// Who to transfer unused protocol fees to.
// May be a valid address or one of:
// `address(0)`: Stay in flash wallet.
@@ -123,7 +128,7 @@ contract FillQuoteTransformer is
uint256 soldAmount;
uint256 protocolFee;
uint256 takerTokenBalanceRemaining;
uint256[3] currentIndices;
uint256[4] currentIndices;
OrderType currentOrderType;
}
@@ -133,7 +138,7 @@ contract FillQuoteTransformer is
event ProtocolFeeUnfunded(bytes32 orderHash);
/// @dev The highest bit of a uint256 value.
uint256 private constant HIGH_BIT = 2 ** 255;
uint256 private constant HIGH_BIT = 2**255;
/// @dev Mask of the lower 255 bits of a uint256 value.
uint256 private constant LOWER_255_BITS = HIGH_BIT - 1;
/// @dev If `refundReceiver` is set to this address, unpsent
@@ -147,15 +152,12 @@ contract FillQuoteTransformer is
IBridgeAdapter public immutable bridgeAdapter;
/// @dev The exchange proxy contract.
INativeOrdersFeature public immutable zeroEx;
IZeroEx public immutable zeroEx;
/// @dev Create this contract.
/// @param bridgeAdapter_ The bridge adapter contract.
/// @param zeroEx_ The Exchange Proxy contract.
constructor(IBridgeAdapter bridgeAdapter_, INativeOrdersFeature zeroEx_)
public
Transformer()
{
constructor(IBridgeAdapter bridgeAdapter_, IZeroEx zeroEx_) public Transformer() {
bridgeAdapter = bridgeAdapter_;
zeroEx = zeroEx_;
}
@@ -165,30 +167,27 @@ contract FillQuoteTransformer is
/// to this call. `buyToken` and excess ETH will be transferred back to the caller.
/// @param context Context information.
/// @return magicBytes The success bytes (`LibERC20Transformer.TRANSFORMER_SUCCESS`).
function transform(TransformContext calldata context)
external
override
returns (bytes4 magicBytes)
{
function transform(TransformContext calldata context) external override returns (bytes4 magicBytes) {
TransformData memory data = abi.decode(context.data, (TransformData));
FillState memory state;
// Validate data fields.
if (data.sellToken.isTokenETH() || data.buyToken.isTokenETH()) {
LibTransformERC20RichErrors.InvalidTransformDataError(
LibTransformERC20RichErrors.InvalidTransformDataErrorCode.INVALID_TOKENS,
context.data
).rrevert();
LibTransformERC20RichErrors
.InvalidTransformDataError(
LibTransformERC20RichErrors.InvalidTransformDataErrorCode.INVALID_TOKENS,
context.data
)
.rrevert();
}
if (data.bridgeOrders.length
+ data.limitOrders.length
+ data.rfqOrders.length != data.fillSequence.length
) {
LibTransformERC20RichErrors.InvalidTransformDataError(
LibTransformERC20RichErrors.InvalidTransformDataErrorCode.INVALID_ARRAY_LENGTH,
context.data
).rrevert();
if (data.bridgeOrders.length + data.limitOrders.length + data.rfqOrders.length != data.fillSequence.length) {
LibTransformERC20RichErrors
.InvalidTransformDataError(
LibTransformERC20RichErrors.InvalidTransformDataErrorCode.INVALID_ARRAY_LENGTH,
context.data
)
.rrevert();
}
state.takerTokenBalanceRemaining = data.sellToken.getTokenBalanceOf(address(this));
@@ -202,8 +201,7 @@ contract FillQuoteTransformer is
data.sellToken.approveIfBelow(address(zeroEx), data.fillAmount);
// Compute the protocol fee if a limit order is present.
if (data.limitOrders.length != 0) {
state.protocolFee = uint256(zeroEx.getProtocolFeeMultiplier())
.safeMul(tx.gasprice);
state.protocolFee = uint256(zeroEx.getProtocolFeeMultiplier()).safeMul(tx.gasprice);
}
}
@@ -214,10 +212,14 @@ contract FillQuoteTransformer is
// Check if we've hit our targets.
if (data.side == Side.Sell) {
// Market sell check.
if (state.soldAmount >= data.fillAmount) { break; }
if (state.soldAmount >= data.fillAmount) {
break;
}
} else {
// Market buy check.
if (state.boughtAmount >= data.fillAmount) { break; }
if (state.boughtAmount >= data.fillAmount) {
break;
}
}
state.currentOrderType = OrderType(data.fillSequence[i]);
@@ -230,19 +232,17 @@ contract FillQuoteTransformer is
results = _fillLimitOrder(data.limitOrders[orderIndex], data, state);
} else if (state.currentOrderType == OrderType.Rfq) {
results = _fillRfqOrder(data.rfqOrders[orderIndex], data, state);
} else if (state.currentOrderType == OrderType.Otc) {
results = _fillOtcOrder(data.otcOrders[orderIndex], data, state);
} else {
revert("INVALID_ORDER_TYPE");
revert('INVALID_ORDER_TYPE');
}
// Accumulate totals.
state.soldAmount = state.soldAmount
.safeAdd(results.takerTokenSoldAmount);
state.boughtAmount = state.boughtAmount
.safeAdd(results.makerTokenBoughtAmount);
state.ethRemaining = state.ethRemaining
.safeSub(results.protocolFeePaid);
state.takerTokenBalanceRemaining = state.takerTokenBalanceRemaining
.safeSub(results.takerTokenSoldAmount);
state.soldAmount = state.soldAmount.safeAdd(results.takerTokenSoldAmount);
state.boughtAmount = state.boughtAmount.safeAdd(results.makerTokenBoughtAmount);
state.ethRemaining = state.ethRemaining.safeSub(results.protocolFeePaid);
state.takerTokenBalanceRemaining = state.takerTokenBalanceRemaining.safeSub(results.takerTokenSoldAmount);
state.currentIndices[uint256(state.currentOrderType)]++;
}
@@ -251,21 +251,15 @@ contract FillQuoteTransformer is
// Market sell check.
if (state.soldAmount < data.fillAmount) {
LibTransformERC20RichErrors
.IncompleteFillSellQuoteError(
address(data.sellToken),
state.soldAmount,
data.fillAmount
).rrevert();
.IncompleteFillSellQuoteError(address(data.sellToken), state.soldAmount, data.fillAmount)
.rrevert();
}
} else {
// Market buy check.
if (state.boughtAmount < data.fillAmount) {
LibTransformERC20RichErrors
.IncompleteFillBuyQuoteError(
address(data.buyToken),
state.boughtAmount,
data.fillAmount
).rrevert();
.IncompleteFillBuyQuoteError(address(data.buyToken), state.boughtAmount, data.fillAmount)
.rrevert();
}
}
@@ -273,13 +267,13 @@ contract FillQuoteTransformer is
if (state.ethRemaining > 0 && data.refundReceiver != address(0)) {
bool transferSuccess;
if (data.refundReceiver == REFUND_RECEIVER_RECIPIENT) {
(transferSuccess,) = context.recipient.call{value: state.ethRemaining}("");
(transferSuccess, ) = context.recipient.call{ value: state.ethRemaining }('');
} else if (data.refundReceiver == REFUND_RECEIVER_SENDER) {
(transferSuccess,) = context.sender.call{value: state.ethRemaining}("");
(transferSuccess, ) = context.sender.call{ value: state.ethRemaining }('');
} else {
(transferSuccess,) = data.refundReceiver.call{value: state.ethRemaining}("");
(transferSuccess, ) = data.refundReceiver.call{ value: state.ethRemaining }('');
}
require(transferSuccess, "FillQuoteTransformer/ETHER_TRANSFER_FALIED");
require(transferSuccess, 'FillQuoteTransformer/ETHER_TRANSFER_FALIED');
}
return LibERC20Transformer.TRANSFORMER_SUCCESS;
}
@@ -289,10 +283,7 @@ contract FillQuoteTransformer is
IBridgeAdapter.BridgeOrder memory order,
TransformData memory data,
FillState memory state
)
private
returns (FillOrderResults memory results)
{
) private returns (FillOrderResults memory results) {
uint256 takerTokenFillAmount = _computeTakerTokenFillAmount(
data,
state,
@@ -321,10 +312,7 @@ contract FillQuoteTransformer is
LimitOrderInfo memory orderInfo,
TransformData memory data,
FillState memory state
)
private
returns (FillOrderResults memory results)
{
) private returns (FillOrderResults memory results) {
uint256 takerTokenFillAmount = LibSafeMathV06.min256(
_computeTakerTokenFillAmount(
data,
@@ -344,22 +332,21 @@ contract FillQuoteTransformer is
}
try
zeroEx.fillLimitOrder
{value: state.protocolFee}
(
orderInfo.order,
orderInfo.signature,
takerTokenFillAmount.safeDowncastToUint128()
)
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount)
{
zeroEx.fillLimitOrder{ value: state.protocolFee }(
orderInfo.order,
orderInfo.signature,
takerTokenFillAmount.safeDowncastToUint128()
)
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount) {
if (orderInfo.order.takerTokenFeeAmount > 0) {
takerTokenFilledAmount = takerTokenFilledAmount.safeAdd128(
LibMathV06.getPartialAmountFloor(
takerTokenFilledAmount,
orderInfo.order.takerAmount,
orderInfo.order.takerTokenFeeAmount
).safeDowncastToUint128()
LibMathV06
.getPartialAmountFloor(
takerTokenFilledAmount,
orderInfo.order.takerAmount,
orderInfo.order.takerTokenFeeAmount
)
.safeDowncastToUint128()
);
}
results.takerTokenSoldAmount = takerTokenFilledAmount;
@@ -373,30 +360,34 @@ contract FillQuoteTransformer is
RfqOrderInfo memory orderInfo,
TransformData memory data,
FillState memory state
)
private
returns (FillOrderResults memory results)
{
) private returns (FillOrderResults memory results) {
uint256 takerTokenFillAmount = LibSafeMathV06.min256(
_computeTakerTokenFillAmount(
data,
state,
orderInfo.order.takerAmount,
orderInfo.order.makerAmount,
0
),
_computeTakerTokenFillAmount(data, state, orderInfo.order.takerAmount, orderInfo.order.makerAmount, 0),
orderInfo.maxTakerTokenFillAmount
);
try
zeroEx.fillRfqOrder
(
orderInfo.order,
orderInfo.signature,
takerTokenFillAmount.safeDowncastToUint128()
)
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount)
{
zeroEx.fillRfqOrder(orderInfo.order, orderInfo.signature, takerTokenFillAmount.safeDowncastToUint128())
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount) {
results.takerTokenSoldAmount = takerTokenFilledAmount;
results.makerTokenBoughtAmount = makerTokenFilledAmount;
} catch {}
}
// Fill a single OTC order.
function _fillOtcOrder(
OtcOrderInfo memory orderInfo,
TransformData memory data,
FillState memory state
) private returns (FillOrderResults memory results) {
uint256 takerTokenFillAmount = LibSafeMathV06.min256(
_computeTakerTokenFillAmount(data, state, orderInfo.order.takerAmount, orderInfo.order.makerAmount, 0),
orderInfo.maxTakerTokenFillAmount
);
try
zeroEx.fillOtcOrder(orderInfo.order, orderInfo.signature, takerTokenFillAmount.safeDowncastToUint128())
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount) {
results.takerTokenSoldAmount = takerTokenFilledAmount;
results.makerTokenBoughtAmount = makerTokenFilledAmount;
} catch {}
@@ -409,11 +400,7 @@ contract FillQuoteTransformer is
uint256 orderTakerAmount,
uint256 orderMakerAmount,
uint256 orderTakerTokenFeeAmount
)
private
pure
returns (uint256 takerTokenFillAmount)
{
) private pure returns (uint256 takerTokenFillAmount) {
if (data.side == Side.Sell) {
takerTokenFillAmount = data.fillAmount.safeSub(state.soldAmount);
if (orderTakerTokenFeeAmount != 0) {
@@ -423,34 +410,31 @@ contract FillQuoteTransformer is
orderTakerAmount
);
}
} else { // Buy
} else {
// Buy
takerTokenFillAmount = LibMathV06.getPartialAmountCeil(
data.fillAmount.safeSub(state.boughtAmount),
orderMakerAmount,
orderTakerAmount
);
}
return LibSafeMathV06.min256(
LibSafeMathV06.min256(takerTokenFillAmount, orderTakerAmount),
state.takerTokenBalanceRemaining
);
return
LibSafeMathV06.min256(
LibSafeMathV06.min256(takerTokenFillAmount, orderTakerAmount),
state.takerTokenBalanceRemaining
);
}
// Convert possible proportional values to absolute quantities.
function _normalizeFillAmount(uint256 rawAmount, uint256 balance)
private
pure
returns (uint256 normalized)
{
function _normalizeFillAmount(uint256 rawAmount, uint256 balance) private pure returns (uint256 normalized) {
if ((rawAmount & HIGH_BIT) == HIGH_BIT) {
// If the high bit of `rawAmount` is set then the lower 255 bits
// specify a fraction of `balance`.
return LibSafeMathV06.min256(
balance
* LibSafeMathV06.min256(rawAmount & LOWER_255_BITS, 1e18)
/ 1e18,
balance
);
return
LibSafeMathV06.min256(
(balance * LibSafeMathV06.min256(rawAmount & LOWER_255_BITS, 1e18)) / 1e18,
balance
);
}
return rawAmount;
}

View File

@@ -83,6 +83,7 @@
},
"dependencies": {
"@0x/base-contract": "^6.5.0",
"@0x/contracts-utils": "^4.8.12",
"@0x/protocol-utils": "^11.13.0",
"@0x/subproviders": "^6.6.5",
"@0x/types": "^3.3.6",

View File

@@ -16,6 +16,8 @@ import {
FillQuoteTransformerSide as Side,
LimitOrder,
LimitOrderFields,
OtcOrder,
OtcOrderFields,
RfqOrder,
RfqOrderFields,
Signature,
@@ -26,10 +28,11 @@ import * as _ from 'lodash';
import { artifacts } from '../artifacts';
import { TestFillQuoteTransformerBridgeContract } from '../generated-wrappers/test_fill_quote_transformer_bridge';
import { getRandomLimitOrder, getRandomRfqOrder } from '../utils/orders';
import { getRandomLimitOrder, getRandomRfqOrder, getRandomOtcOrder } from '../utils/orders';
import {
BridgeAdapterContract,
FillQuoteTransformerContract,
OtcOrdersFeatureContract,
TestFillQuoteTransformerExchangeContract,
TestFillQuoteTransformerHostContract,
TestMintableERC20TokenContract,
@@ -71,6 +74,15 @@ blockchainTests.resets('FillQuoteTransformer', env => {
artifacts,
NULL_ADDRESS,
);
const otcOrder = await OtcOrdersFeatureContract.deployFrom0xArtifactAsync(
artifacts.OtcOrdersFeature,
env.provider,
env.txDefaults,
artifacts,
NULL_ADDRESS,
NULL_ADDRESS, // weth
);
transformer = await FillQuoteTransformerContract.deployFrom0xArtifactAsync(
artifacts.FillQuoteTransformer,
env.provider,
@@ -78,6 +90,7 @@ blockchainTests.resets('FillQuoteTransformer', env => {
artifacts,
bridgeAdapter.address,
exchange.address,
otcOrder.address
);
host = await TestFillQuoteTransformerHostContract.deployFrom0xArtifactAsync(
artifacts.TestFillQuoteTransformerHost,
@@ -141,6 +154,18 @@ blockchainTests.resets('FillQuoteTransformer', env => {
};
}
function createOTCBridgeOrder(fields: Partial<OtcOrderFields> = {}): OtcOrder {
return getRandomOtcOrder({
makerToken: makerToken.address,
takerToken: takerToken.address,
makerAmount: getRandomInteger('0.1e18', '1e18'),
takerAmount: getRandomInteger('0.1e18', '1e18'),
maker,
taker,
...fields,
});
}
function createOrderSignature(preFilledTakerAmount: Numberish = 0): Signature {
return {
// The r field of the signature is the pre-filled amount.
@@ -271,6 +296,24 @@ blockchainTests.resets('FillQuoteTransformer', env => {
};
}
function fillOtcOrder(oi: FillQuoteTransformerRfqOrderInfo): FillOrderResults {
const preFilledTakerAmount = orderSignatureToPreFilledTakerAmount(oi.signature);
if (preFilledTakerAmount.gte(oi.order.takerAmount) || preFilledTakerAmount.eq(REVERT_AMOUNT)) {
return EMPTY_FILL_ORDER_RESULTS;
}
const takerTokenFillAmount = BigNumber.min(
computeTakerTokenFillAmount(oi.order.takerAmount, oi.order.makerAmount),
oi.order.takerAmount.minus(preFilledTakerAmount),
oi.maxTakerTokenFillAmount,
);
const fillRatio = takerTokenFillAmount.div(oi.order.takerAmount);
return {
...EMPTY_FILL_ORDER_RESULTS,
takerTokenSoldAmount: takerTokenFillAmount,
makerTokenBoughtAmount: fillRatio.times(oi.order.makerAmount).integerValue(BigNumber.ROUND_DOWN),
};
}
// tslint:disable-next-line: prefer-for-of
for (let i = 0; i < data.fillSequence.length; ++i) {
const orderType = data.fillSequence[i];

View File

@@ -60,9 +60,10 @@
"dependencies": {
"@0x/assert": "^3.0.34",
"@0x/base-contract": "^6.5.0",
"@0x/contract-addresses": "^6.14.0",
"@0x/contract-wrappers": "^13.20.2",
"@0x/contracts-erc20": "^3.3.30",
"@0x/contract-addresses": "^6.16.0",
"@0x/contract-wrappers": "^13.20.4",
"@0x/contracts-erc20": "^3.3.32",
"@0x/contracts-test-utils": "^5.4.23",
"@0x/contracts-zero-ex": "^0.33.0",
"@0x/dev-utils": "^4.2.14",
"@0x/json-schemas": "^6.4.4",

View File

@@ -42,6 +42,7 @@ import {
FinalUniswapV3FillData,
LiquidityProviderFillData,
MooniswapFillData,
NativeOtcOrderFillData,
NativeRfqOrderFillData,
OptimizedMarketBridgeOrder,
OptimizedMarketOrder,
@@ -377,7 +378,66 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
gasOverhead: ZERO_AMOUNT,
};
}
if (
// select for all chains OtcOrders exists on
[ChainId.Mainnet].includes(this.chainId) &&
quote.orders.length == 1 &&
quote.orders.every(o => o.type === FillQuoteTransformerOrderType.Otc) &&
!requiresTransformERC20(optsWithDefaults)
) {
const otcOrdersData = quote.orders.map(o => o.fillData as NativeOtcOrderFillData);
const fillAmountPerOrder = (() => {
// Don't think order taker amounts are clipped to actual sell amount
// (the last one might be too large) so figure them out manually.
let remaining = sellAmount;
const fillAmounts = [];
for (const o of quote.orders) {
const fillAmount = BigNumber.min(o.takerAmount, remaining);
fillAmounts.push(fillAmount);
remaining = remaining.minus(fillAmount);
}
return fillAmounts;
})();
// grab the amount to fill on each OtcOrder (if more than 1)
let calldata;
if(isFromETH){
calldata = this._exchangeProxy.fillOtcOrderWithEth(
otcOrdersData[0].order, otcOrdersData[0].signature
).getABIEncodedTransactionData();
}
if(isToETH){
calldata = this._exchangeProxy.fillOtcOrderForEth(
otcOrdersData[0].order, otcOrdersData[0].signature, fillAmountPerOrder[0]
).getABIEncodedTransactionData();
}
else{
calldata = this._exchangeProxy.fillOtcOrder(
otcOrdersData[0].order, otcOrdersData[0].signature, fillAmountPerOrder[0]
).getABIEncodedTransactionData();
}
if (
this.chainId === ChainId.Mainnet &&
isMultiplexBatchFillCompatible(quote, optsWithDefaults)) {
// return {
// calldataHexString: this._encodeMultiplexBatchFillCalldata(
// ...
// };
}
// if isToETH
// encode for fillOtcOrderForEth
// if isFromETH
// encode for fillOtcOrderWithEth
// else
// fillOtcOrder
// contracts/zero-ex/contracts/src/features/OtcOrdersFeature.sol
// if more than 1 OTCOrder, bail and use the BatchMultiPlex encode below
}
if (this.chainId === ChainId.Mainnet && isMultiplexBatchFillCompatible(quote, optsWithDefaults)) {
return {
calldataHexString: this._encodeMultiplexBatchFillCalldata(

View File

@@ -5,6 +5,7 @@ import {
LimitOrderFields,
RfqOrder,
RfqOrderFields,
OtcOrderFields,
Signature,
} from '@0x/protocol-utils';
import { TakerRequestQueryParamsUnnested, V4SignedRfqOrder } from '@0x/quote-server';
@@ -34,11 +35,11 @@ export interface OrderPrunerOpts {
export interface SignedOrder<T> {
order: T;
type: FillQuoteTransformerOrderType.Limit | FillQuoteTransformerOrderType.Rfq;
type: FillQuoteTransformerOrderType.Limit | FillQuoteTransformerOrderType.Rfq | FillQuoteTransformerOrderType.Otc;
signature: Signature;
}
export type SignedNativeOrder = SignedOrder<LimitOrderFields> | SignedOrder<RfqOrderFields>;
export type SignedNativeOrder = SignedOrder<LimitOrderFields> | SignedOrder<RfqOrderFields> | SignedOrder<OtcOrderFields>;
export type NativeOrderWithFillableAmounts = SignedNativeOrder & NativeOrderFillableAmountFields;
/**

View File

@@ -1,4 +1,4 @@
import { RfqOrder, Signature } from '@0x/protocol-utils';
import { OtcOrder, RfqOrder, Signature } from '@0x/protocol-utils';
import { BigNumber } from '@0x/utils';
import { AltRfqMakerAssetOfferings } from '../types';
@@ -43,6 +43,48 @@ export interface RfqClientV1QuoteResponse {
quotes: RfqClientV1Quote[];
}
export interface RfqClientV2PriceRequest {
assetFillAmount: BigNumber;
chainId: number;
comparisonPrice: BigNumber | undefined;
integratorId: string;
intentOnFilling: boolean;
makerToken: string;
marketOperation: 'Sell' | 'Buy';
takerAddress: string;
takerToken: string;
txOrigin: string;
}
export interface RfqClientV2QuoteRequest extends RfqClientV2PriceRequest {}
export interface RfqClientV2Price {
expiry: BigNumber;
maker: string;
makerAmount: BigNumber;
makerToken: string;
makerUri: string;
takerAmount: BigNumber;
takerToken: string;
}
export interface RfqClientV2PriceResponse {
prices: RfqClientV2Price[];
}
export interface RfqClientV2Quote {
makerUri: string;
order: OtcOrder;
signature: Signature;
fillableMakerAmount: BigNumber;
fillableTakerAmount: BigNumber;
fillableTakerFeeAmount: BigNumber;
}
export interface RfqClientV2QuoteResponse {
quotes: RfqClientV2Quote[];
}
/**
* IRfqClient is an interface that defines how to connect with an Rfq system.
*/
@@ -56,4 +98,14 @@ export interface IRfqClient {
* Fetches a list of "firm quotes" or signed quotes from a remote Rfq server.
*/
getV1QuotesAsync(request: RfqClientV1QuoteRequest): Promise<RfqClientV1QuoteResponse>;
/**
* Fetches a list of "v2 indicative quotes" or prices from a remote Rfq server
*/
getV2PricesAsync(request: RfqClientV2PriceRequest): Promise<RfqClientV2PriceResponse>;
/**
* Fetches a list of "v2 firm quotes" or signed quotes from a remote Rfq server.
*/
getV2QuotesAsync(request: RfqClientV2QuoteRequest): Promise<RfqClientV2QuoteResponse>;
}

View File

@@ -641,6 +641,7 @@ export const OPTIMISM_TOKENS = {
WBTC: '0x68f180fcce6836688e9084f035309e29bf0a2095',
nETH: '0x809dc529f07651bd43a172e8db6f4a7a0d771036',
sWETH: '0x121ab82b49b2bc4c7901ca46b8277962b4350204',
nUSD: '0x67C10C397dD0Ba417329543c1a40eb48AAa7cd00',
};
export const CURVE_POOLS = {
@@ -770,6 +771,7 @@ export const SYNAPSE_MAINNET_POOLS = {
export const SYNAPSE_OPTIMISM_POOLS = {
nETHLP: '0xe27bff97ce92c3e1ff7aa9f86781fdd6d48f5ee9',
nUSDLP: '0xF44938b0125A6662f9536281aD2CD6c499F22004',
};
export const SYNAPSE_BSC_POOLS = {
@@ -783,6 +785,7 @@ export const SYNAPSE_POLYGON_POOLS = {
export const SYNAPSE_FANTOM_POOLS = {
nUSDLP: '0x2913e812cf0dcca30fb28e6cac3d2dcff4497688',
nETHLP: '0x8d9ba570d6cb60c7e3e0f31343efe75ab8e65fb1',
fUSDTLP: '0x85662fd123280827e11c59973ac9fcbe838dc3b4',
};
export const SYNAPSE_AVALANCHE_POOLS = {
@@ -1654,6 +1657,24 @@ export const SYNAPSE_FANTOM_INFOS: { [name: string]: CurveInfo } = {
metaTokens: undefined,
gasSchedule: 140e3,
},
[SYNAPSE_FANTOM_POOLS.fUSDTLP]: {
exchangeFunctionSelector: CurveFunctionSelectors.swap,
sellQuoteFunctionSelector: CurveFunctionSelectors.calculateSwap,
buyQuoteFunctionSelector: CurveFunctionSelectors.None,
poolAddress: SYNAPSE_FANTOM_POOLS.fUSDTLP,
tokens: [FANTOM_TOKENS.USDC, FANTOM_TOKENS.fUSDT, FANTOM_TOKENS.nUSD],
metaTokens: undefined,
gasSchedule: 140e3,
},
[SYNAPSE_FANTOM_POOLS.nETHLP]: {
exchangeFunctionSelector: CurveFunctionSelectors.swap,
sellQuoteFunctionSelector: CurveFunctionSelectors.calculateSwap,
buyQuoteFunctionSelector: CurveFunctionSelectors.None,
poolAddress: SYNAPSE_FANTOM_POOLS.nETHLP,
tokens: [FANTOM_TOKENS.WETH, FANTOM_TOKENS.nETH],
metaTokens: undefined,
gasSchedule: 140e3,
},
};
export const SYNAPSE_MAINNET_INFOS: { [name: string]: CurveInfo } = {
@@ -1678,6 +1699,15 @@ export const SYNAPSE_OPTIMISM_INFOS: { [name: string]: CurveInfo } = {
metaTokens: undefined,
gasSchedule: 140e3,
},
[SYNAPSE_OPTIMISM_POOLS.nUSDLP]: {
exchangeFunctionSelector: CurveFunctionSelectors.swap,
sellQuoteFunctionSelector: CurveFunctionSelectors.calculateSwap,
buyQuoteFunctionSelector: CurveFunctionSelectors.None,
poolAddress: SYNAPSE_OPTIMISM_POOLS.nUSDLP,
tokens: [OPTIMISM_TOKENS.nUSD, OPTIMISM_TOKENS.USDC],
metaTokens: undefined,
gasSchedule: 140e3,
},
};
export const SYNAPSE_POLYGON_INFOS: { [name: string]: CurveInfo } = {

View File

@@ -747,6 +747,26 @@ export class MarketOperationUtils {
wholeOrderPrice,
rfqt,
);
const otcQuotes = await rfqt.rfqClient?.getV2QuotesAsync({
assetFillAmount: amount,
chainId: this._sampler.chainId,
integratorId: rfqt.integrator.integratorId,
intentOnFilling: rfqt.intentOnFilling,
makerToken,
marketOperation: side,
takerAddress: rfqt.takerAddress,
takerToken,
txOrigin: rfqt.txOrigin,
});
const otcQuotesWithFillableAmounts: NativeOrderWithFillableAmounts[] =
otcQuotes === undefined
? []
: otcQuotes.quotes.map(q => ({
...q,
type: FillQuoteTransformerOrderType.Otc,
}));
const deltaTime = new Date().getTime() - timeStart;
DEFAULT_INFO_LOGGER({
rfqQuoteType: 'firm',
@@ -777,6 +797,7 @@ export class MarketOperationUtils {
);
marketSideLiquidity.quotes.nativeOrders = [
...quotesWithOrderFillableAmounts,
...otcQuotesWithFillableAmounts,
...marketSideLiquidity.quotes.nativeOrders,
];

View File

@@ -2,6 +2,7 @@ import {
FillQuoteTransformerLimitOrderInfo,
FillQuoteTransformerOrderType,
FillQuoteTransformerRfqOrderInfo,
FillQuoteTransformerOtcOrderInfo
} from '@0x/protocol-utils';
import { MarketOperation } from '@0x/types';
import { BigNumber } from '@0x/utils';
@@ -195,7 +196,9 @@ export interface FillData {}
// `FillData` for native fills. Represents a single native order
export type NativeRfqOrderFillData = FillQuoteTransformerRfqOrderInfo;
export type NativeLimitOrderFillData = FillQuoteTransformerLimitOrderInfo;
export type NativeFillData = NativeRfqOrderFillData | NativeLimitOrderFillData;
export type NativeOtcOrderFillData = FillQuoteTransformerOtcOrderInfo;
export type NativeFillData = NativeRfqOrderFillData | NativeLimitOrderFillData | NativeOtcOrderFillData;
// Represents an individual DEX sample from the sampler contract
export interface DexSample<TFillData extends FillData = FillData> {
@@ -454,13 +457,19 @@ export interface OptimizedRfqOrder extends OptimizedMarketOrderBase<NativeRfqOrd
fillData: NativeRfqOrderFillData;
}
export interface OptimizedOtcOrder extends OptimizedMarketOrderBase<NativeOtcOrderFillData> {
type: FillQuoteTransformerOrderType.Otc;
fillData: NativeOtcOrderFillData;
}
/**
* Optimized orders to fill.
*/
export type OptimizedMarketOrder =
| OptimizedMarketBridgeOrder<FillData>
| OptimizedMarketOrderBase<NativeLimitOrderFillData>
| OptimizedMarketOrderBase<NativeRfqOrderFillData>;
| OptimizedMarketOrderBase<NativeRfqOrderFillData>
| OptimizedMarketOrderBase<NativeOtcOrderFillData>;
export interface GetMarketOrdersRfqOpts extends RfqRequestOpts {
rfqClient?: IRfqClient;

View File

@@ -13,6 +13,7 @@ import {
NativeCollapsedFill,
NativeFillData,
NativeLimitOrderFillData,
NativeOtcOrderFillData,
NativeRfqOrderFillData,
RawQuotes,
} from './market_operation_utils/types';
@@ -353,7 +354,7 @@ function _isNativeOrderFromCollapsedFill(cf: CollapsedFill): cf is NativeCollaps
*/
export function nativeOrderToReportEntry(
type: FillQuoteTransformerOrderType,
fillData: NativeLimitOrderFillData | NativeRfqOrderFillData,
fillData: NativeLimitOrderFillData | NativeRfqOrderFillData | NativeOtcOrderFillData,
fillableAmount: BigNumber,
comparisonPrice?: BigNumber | undefined,
quoteRequestor?: QuoteRequestor,

View File

@@ -63,7 +63,7 @@
},
"dependencies": {
"@0x/assert": "^3.0.34",
"@0x/contract-addresses": "^6.14.0",
"@0x/contract-addresses": "^6.16.0",
"@0x/contract-wrappers": "^13.20.2",
"@0x/json-schemas": "^6.4.4",
"@0x/subproviders": "^6.6.5",

View File

@@ -63,7 +63,7 @@ export type LimitOrderFields = typeof LIMIT_ORDER_DEFAULT_VALUES;
export type RfqOrderFields = typeof RFQ_ORDER_DEFAULT_VALUES;
export type OtcOrderFields = typeof OTC_ORDER_DEFAULT_VALUES;
export type BridgeOrderFields = typeof BRIDGE_ORDER_DEFAULT_VALUES;
export type NativeOrder = RfqOrder | LimitOrder;
export type NativeOrder = RfqOrder | LimitOrder | OtcOrder;
export enum OrderStatus {
Invalid = 0,

View File

@@ -1,7 +1,7 @@
import { AbiEncoder, BigNumber, hexUtils, NULL_ADDRESS } from '@0x/utils';
import * as ethjs from 'ethereumjs-util';
import { LimitOrder, LimitOrderFields, RfqOrder, RfqOrderFields } from './orders';
import { LimitOrder, LimitOrderFields, RfqOrder, RfqOrderFields, OtcOrder, OtcOrderFields } from './orders';
import { Signature, SIGNATURE_ABI } from './signature_utils';
const BRIDGE_ORDER_ABI_COMPONENTS = [
@@ -39,6 +39,20 @@ const RFQ_ORDER_INFO_ABI_COMPONENTS = [
{ name: 'maxTakerTokenFillAmount', type: 'uint256' },
];
const OTC_ORDER_INFO_ABI_COMPONENTS = [
{
name: 'order',
type: 'tuple',
components: OtcOrder.STRUCT_ABI,
},
{
name: 'signature',
type: 'tuple',
components: SIGNATURE_ABI,
},
{ name: 'maxTakerTokenFillAmount', type: 'uint256' },
];
/**
* ABI encoder for `FillQuoteTransformer.TransformData`
*/
@@ -65,6 +79,11 @@ export const fillQuoteTransformerDataEncoder = AbiEncoder.create([
type: 'tuple[]',
components: RFQ_ORDER_INFO_ABI_COMPONENTS,
},
{
name: 'otcOrders',
type: 'tuple[]',
components: OTC_ORDER_INFO_ABI_COMPONENTS,
},
{ name: 'fillSequence', type: 'uint8[]' },
{ name: 'fillAmount', type: 'uint256' },
{ name: 'refundReceiver', type: 'address' },
@@ -87,6 +106,7 @@ export enum FillQuoteTransformerOrderType {
Bridge,
Limit,
Rfq,
Otc,
}
/**
@@ -170,6 +190,11 @@ export type FillQuoteTransformerLimitOrderInfo = FillQuoteTransformerNativeOrder
*/
export type FillQuoteTransformerRfqOrderInfo = FillQuoteTransformerNativeOrderInfo<RfqOrderFields>;
/**
* `FillQuoteTransformer.OtcOrderInfo`
*/
export type FillQuoteTransformerOtcOrderInfo = FillQuoteTransformerNativeOrderInfo<OtcOrderFields>;
/**
* ABI-encode a `FillQuoteTransformer.TransformData` type.
*/
@@ -177,6 +202,8 @@ export function encodeFillQuoteTransformerData(data: FillQuoteTransformerData):
return fillQuoteTransformerDataEncoder.encode([data]);
}
/**
* ABI-decode a `FillQuoteTransformer.TransformData` type.
*/
@@ -184,6 +211,7 @@ export function decodeFillQuoteTransformerData(encoded: string): FillQuoteTransf
return fillQuoteTransformerDataEncoder.decode(encoded).data;
}
/**
* ABI encoder for `WethTransformer.TransformData`
*/

View File

@@ -4,7 +4,7 @@ import { BigNumber } from '@0x/utils';
import { expect } from 'chai';
import * as ethjs from 'ethereumjs-util';
import { LimitOrder, RfqOrder } from '../src/orders';
import { LimitOrder, OtcOrder, RfqOrder } from '../src/orders';
import { SignatureType } from '../src/signature_utils';
chaiSetup.configure();
@@ -145,4 +145,64 @@ describe('orders', () => {
expect(actual).to.deep.eq(expected);
});
});
describe('OtcOrder', () => {
const order = new OtcOrder({
makerToken: '0x349e8d89e8b37214d9ce3949fc5754152c525bc3',
takerToken: '0x83c62b2e67dea0df2a27be0def7a22bd7102642c',
makerAmount: new BigNumber(1234),
takerAmount: new BigNumber(5678),
maker: '0x8d5e5b5b5d187bdce2e0143eb6b3cc44eef3c0cb',
taker: '0x615312fb74c31303eab07dea520019bb23f4c6c2',
txOrigin: '0x70f2d6c7acd257a6700d745b76c602ceefeb8e20',
expiryAndNonce: new BigNumber(1001),
chainId: 8008,
verifyingContract: '0x6701704d2421c64ee9aa93ec7f96ede81c4be77d',
});
it('can get the struct hash', () => {
const actual = order.getStructHash();
// const expected = '0x995b6261fa93cd5acd5121f404305f8e9f9c388723f3e53fb05bd5eb534b4899';
// expect(actual).to.eq(expected);
});
it('can get the EIP712 hash', () => {
const actual = order.getHash();
// const expected = '0xb4c40524740dcc4030a62b6d9afe740f6ca24508e59ef0c5bd99d5649a430885';
// expect(actual).to.deep.eq(expected);
});
it('can get an EthSign signature with a provider', async () => {
const actual = await order.clone({ maker: providerMaker }).getSignatureWithProviderAsync(provider);
// const expected = {
// signatureType: SignatureType.EthSign,
// r: '0xed555259efe38e2d679f7bc18385e51ce158576ced6c11630f67ba37b3e59a29',
// s: '0x769211cf3e86b254e3755e1dcf459f5b362ca1c42ec3cf08841d90cb44f2a8e4',
// v: 27,
// };
// expect(actual).to.deep.eq(expected);
});
it('can get an EthSign signature with a private key', () => {
const actual = order.clone({ maker: keyMaker }).getSignatureWithKey(key);
// const expected = {
// signatureType: SignatureType.EthSign,
// r: '0xba231f67168d6d1fd2b83e0a3a6b1663ec493b98a8dbe34689c8e8171972522f',
// s: '0x47023a5f73b5f638e9a138de26b35e59847680bee78af0c8251de532e7c39d8b',
// v: 28,
// };
// expect(actual).to.deep.eq(expected);
});
it('can get an EIP712 signature with a private key', () => {
const actual = order.clone({ maker: keyMaker }).getSignatureWithKey(key, SignatureType.EIP712);
// const expected = {
// signatureType: SignatureType.EIP712,
// r: '0x824d70ae7cccea382ddd51f773f9745abb928dadbccebbd090ca371d7b8fb741',
// s: '0x7557a009f7cfa207d19a8fd42950458340de718a7b35522051cde6f75ad42cba',
// v: 27,
// };
// expect(actual).to.deep.eq(expected);
});
});
});

View File

@@ -732,6 +732,26 @@
js-sha3 "^0.7.0"
uuid "^3.3.2"
"@0x/contract-addresses@^6.16.0":
version "6.16.0"
resolved "https://registry.yarnpkg.com/@0x/contract-addresses/-/contract-addresses-6.16.0.tgz#dc8dc4c5319f7eee40e10ccb462a254b6eb03b14"
integrity sha512-Gsc/9EttCUtemiJR5/U1JPezxVUtlQ3pq6rPkc7YJL0isK0AwYIrQm82b6Z8wyg9bPMs9dkONc806nnUehY5pQ==
"@0x/contract-wrappers@^13.20.4":
version "13.20.4"
resolved "https://registry.yarnpkg.com/@0x/contract-wrappers/-/contract-wrappers-13.20.4.tgz#e77e6bc4be2c0288fe6846cf7408a1694567e7e2"
integrity sha512-kbaYHjjgx1MN2+JRipmo6crl8p4lZpFyVFFp2ULtcEFBEzi0UIEMxo7SXCEguMe/yOY7NGGuLUMJ3Zd3IEjCxA==
dependencies:
"@0x/assert" "^3.0.34"
"@0x/base-contract" "^6.5.0"
"@0x/contract-addresses" "^6.16.0"
"@0x/json-schemas" "^6.4.4"
"@0x/types" "^3.3.6"
"@0x/utils" "^6.5.3"
"@0x/web3-wrapper" "^7.6.5"
ethereum-types "^3.7.0"
ethers "~4.0.4"
"@0x/contracts-asset-proxy@^3.7.19":
version "3.7.19"
resolved "https://registry.yarnpkg.com/@0x/contracts-asset-proxy/-/contracts-asset-proxy-3.7.19.tgz#ee621a233f4d77b439c74c5b8d70db2e1ed001c4"
@@ -783,6 +803,14 @@
"@0x/web3-wrapper" "^7.5.3"
lodash "^4.17.11"
"@0x/contracts-erc20@^3.3.32":
version "3.3.32"
resolved "https://registry.yarnpkg.com/@0x/contracts-erc20/-/contracts-erc20-3.3.32.tgz#e389594fe66722f4ad05ef9f5ebc581f35ea3fb2"
integrity sha512-SK2vAyXxDU4HsEB0rjC2/NQJadournmw7VksofY1GxbGYdHPcp2VHdGAbbncCGRW56DByS89RVphH9JIsy5Fhg==
dependencies:
"@0x/base-contract" "^6.5.0"
ethers "~4.0.4"
"@0x/contracts-erc721@^3.1.37":
version "3.1.37"
resolved "https://registry.yarnpkg.com/@0x/contracts-erc721/-/contracts-erc721-3.1.37.tgz#d7d356737e3d2752cf49be385237fbf7d0c5745c"
@@ -867,6 +895,55 @@
ethereum-types "^3.5.0"
ethereumjs-util "^7.0.10"
"@0x/contracts-test-utils@^5.4.23":
version "5.4.23"
resolved "https://registry.yarnpkg.com/@0x/contracts-test-utils/-/contracts-test-utils-5.4.23.tgz#515e120646cbba6644fa7c67b3259736b6c88601"
integrity sha512-cmLalK8MV3OEzbLq9Jyfc0a13//rR6bVQXtbYCNwb/ygqsP6HOCPQ24T6PLS6JyGb/nPCl2TRrrZ9TDgNb2uOA==
dependencies:
"@0x/assert" "^3.0.34"
"@0x/base-contract" "^6.5.0"
"@0x/contract-addresses" "^6.16.0"
"@0x/dev-utils" "^4.2.14"
"@0x/json-schemas" "^6.4.4"
"@0x/order-utils" "^10.4.28"
"@0x/sol-coverage" "^4.0.45"
"@0x/sol-profiler" "^4.1.35"
"@0x/sol-trace" "^3.0.45"
"@0x/subproviders" "^6.6.5"
"@0x/types" "^3.3.6"
"@0x/typescript-typings" "^5.3.1"
"@0x/utils" "^6.5.3"
"@0x/web3-wrapper" "^7.6.5"
"@types/bn.js" "^4.11.0"
"@types/js-combinatorics" "^0.5.29"
"@types/lodash" "4.14.104"
"@types/mocha" "^5.2.7"
"@types/node" "12.12.54"
bn.js "^4.11.8"
chai "^4.0.1"
chai-as-promised "^7.1.0"
chai-bignumber "^3.0.0"
decimal.js "^10.2.0"
dirty-chai "^2.0.1"
ethereum-types "^3.7.0"
ethereumjs-util "^7.0.10"
ethers "~4.0.4"
js-combinatorics "^0.5.3"
lodash "^4.17.11"
make-promises-safe "^1.1.0"
mocha "^6.2.0"
"@0x/contracts-utils@^4.8.12":
version "4.8.12"
resolved "https://registry.yarnpkg.com/@0x/contracts-utils/-/contracts-utils-4.8.12.tgz#849c2f2f9368a4041c2e2d0c0c0c9716a13383ca"
integrity sha512-CWKBAFcs4dyD33McswwJEsoFwldJc0onLFQyLLpd2rAOlwoWxW6QuvGmtE5LOXOXsTy11kDJTO68dQylIN6Qlw==
dependencies:
"@0x/base-contract" "^6.5.0"
"@0x/typescript-typings" "^5.3.1"
"@0x/utils" "^6.5.3"
bn.js "^4.11.8"
ethereum-types "^3.7.0"
"@0x/dev-utils@^4.2.14":
version "4.2.14"
resolved "https://registry.yarnpkg.com/@0x/dev-utils/-/dev-utils-4.2.14.tgz#2b15b3247ccaf111d8d42689907b603537b0a86c"
@@ -3796,6 +3873,7 @@ bigi@1.4.2, bigi@^1.1.0:
bignumber.js@7.2.1, bignumber.js@^9.0.0, bignumber.js@^9.0.2, bignumber.js@~4.1.0, bignumber.js@~9.0.0, bignumber.js@~9.0.2:
version "9.0.2"
resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.2.tgz#71c6c6bed38de64e24a65ebe16cfcf23ae693673"
integrity sha512-GAcQvbpsM0pUb0zw1EI0KhQEZ+lRwR5fYaAp3vPOYuP7aDvGy6cVN6XHLauvF8SOga2y0dcLcjt3iQDTSEliyw==
binary-extensions@^2.0.0:
version "2.1.0"