Compare commits

..

17 Commits

Author SHA1 Message Date
Github Actions
33e260f9db Publish
- @0x/contracts-integrations@2.7.36
 - @0x/contracts-treasury@1.1.3
 - @0x/contracts-zero-ex@0.22.0
 - @0x/asset-swapper@6.7.0
 - @0x/contract-wrappers-test@12.2.41
 - @0x/migrations@8.0.2
 - @0x/protocol-utils@1.5.0
2021-04-26 23:59:58 +00:00
Github Actions
c44f8d0060 Updated CHANGELOGS & MD docs 2021-04-26 23:59:53 +00:00
Kim Persson
411548a33e feat: PancakeSwap 2 integration [TKR-83] (#211)
* feat: PancakeSwap 2 integration

* chore: add PancakeSwap V2 changelog entry

Co-authored-by: Romain Butteaud <romain@0xproject.com>
2021-04-26 15:51:04 -07:00
Alex Kroeger
9a17ce1383 add registerAllowedSigner to NativeOrdersFeature (#195)
* add registerAllowedSigner to NativeOrdersFeature

* fix PR reference in changelog

* prettier

* add cancel pair orders functions for signers

* refactored cancelPairOrders logic, changed naming of signerRegistry to orderSigner registry everywhere, cleaned up tests

* updated changelog for protocol-utils, made batchCancelPairOrders functions more efficient

* clean up function documentation

* added additional tests for batchCancelPairsWithSigner functions, added additional checks for events being emitted correctly

* fix typos in function documentation

* fix typo in function documentation

* update docs

* update comments on cancel functions, small tweaks to docs
2021-04-26 14:32:35 -07:00
Romain Butteaud
2b120d0669 fix: feeType=positive_slippage logic [TKR-81] (#209)
* fix: Fix feeType=positive_slippage logic to not force the EP route

* fix: yarn prettier
2021-04-26 11:26:57 -07:00
Github Actions
6d877d5242 Publish
- @0x/contracts-integrations@2.7.35
 - @0x/asset-swapper@6.6.0
2021-04-16 17:07:22 +00:00
Github Actions
e4abd690e7 Updated CHANGELOGS & MD docs 2021-04-16 17:07:17 +00:00
Jacob Evans
2a194384b6 [asset-swapper] Support Ropsten testnet (#203) 2021-04-16 13:55:35 +10:00
Jacob Evans
1e069e6f8a Remove 10 days from protocol fees docs (#202) 2021-04-16 10:51:41 +10:00
Daniel Pyrathon
a019bb913d only set Last Look parameter if it's explicitly set (#200)
* only set Last Look parameter if it's explicitly set

* remove expoectation of having isLastLook
2021-04-14 16:38:55 -07:00
Github Actions
9ce73931f7 Publish
- @0x/contracts-integrations@2.7.34
 - @0x/asset-swapper@6.5.3
2021-04-14 20:56:21 +00:00
Github Actions
97020df178 Updated CHANGELOGS & MD docs 2021-04-14 20:56:14 +00:00
Lawrence Forman
dfb7b3de8f Apply slippage to non-native orders [TKR-39] (#198)
* `@0x/asset-swapper`: Apply slippage to FQT bridge orders

* review comments

Co-authored-by: Lawrence Forman <me@merklejerk.com>
2021-04-13 19:24:50 -04:00
Github Actions
9e152912fe Publish
- @0x/contracts-integrations@2.7.33
 - @0x/asset-swapper@6.5.2
2021-04-13 11:51:04 +00:00
Github Actions
b2c2f1e1aa Updated CHANGELOGS & MD docs 2021-04-13 11:50:57 +00:00
Jacob Evans
629c7d8e92 Fix asset-swapper test (#199) 2021-04-13 21:01:26 +10:00
Jacob Evans
62f24d4356 [asset-swapper] add Native fee token on all chains (#191) 2021-04-13 08:21:26 +10:00
49 changed files with 1439 additions and 192 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-integrations",
"version": "2.7.32",
"version": "2.7.36",
"private": true,
"engines": {
"node": ">=6.12"
@@ -65,9 +65,9 @@
"@0x/contracts-utils": "^4.7.6",
"@0x/coordinator-server": "^1.0.5",
"@0x/dev-utils": "^4.2.1",
"@0x/migrations": "^8.0.1",
"@0x/migrations": "^8.0.2",
"@0x/order-utils": "^10.4.19",
"@0x/protocol-utils": "^1.4.1",
"@0x/protocol-utils": "^1.5.0",
"@0x/sol-compiler": "^4.6.1",
"@0x/tslint-config": "^4.1.3",
"@0x/web3-wrapper": "^7.4.1",
@@ -93,7 +93,7 @@
"typescript": "4.2.2"
},
"dependencies": {
"@0x/asset-swapper": "^6.5.1",
"@0x/asset-swapper": "^6.7.0",
"@0x/base-contract": "^6.2.18",
"@0x/contracts-asset-proxy": "^3.7.9",
"@0x/contracts-erc1155": "^2.1.27",
@@ -103,7 +103,7 @@
"@0x/contracts-multisig": "^4.1.28",
"@0x/contracts-staking": "^2.0.35",
"@0x/contracts-test-utils": "^5.3.24",
"@0x/contracts-zero-ex": "^0.21.1",
"@0x/contracts-zero-ex": "^0.22.0",
"@0x/subproviders": "^6.4.1",
"@0x/types": "^3.3.1",
"@0x/typescript-typings": "^5.1.6",

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1619481586,
"version": "1.1.3",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1618259868,
"version": "1.1.2",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v1.1.3 - _April 26, 2021_
* Dependencies updated
## v1.1.2 - _April 12, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-treasury",
"version": "1.1.2",
"version": "1.1.3",
"engines": {
"node": ">=6.12"
},
@@ -73,7 +73,7 @@
},
"dependencies": {
"@0x/base-contract": "^6.2.18",
"@0x/protocol-utils": "^1.4.1",
"@0x/protocol-utils": "^1.5.0",
"@0x/subproviders": "^6.4.1",
"@0x/types": "^3.3.1",
"@0x/typescript-typings": "^5.1.6",

View File

@@ -1,4 +1,14 @@
[
{
"version": "0.22.0",
"changes": [
{
"note": "Add order signer registry to NativeOrdersFeature",
"pr": 195
}
],
"timestamp": 1619481586
},
{
"timestamp": 1618259868,
"version": "0.21.1",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v0.22.0 - _April 26, 2021_
* Add order signer registry to NativeOrdersFeature (#195)
## v0.21.1 - _April 12, 2021_
* Dependencies updated

View File

@@ -88,6 +88,21 @@ library LibNativeOrdersRichErrors {
);
}
function InvalidSignerError(
address maker,
address signer
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
bytes4(keccak256("InvalidSignerError(address,address)")),
maker,
signer
);
}
function OrderNotFillableBySenderError(
bytes32 orderHash,
address sender,

View File

@@ -74,9 +74,13 @@ contract NativeOrdersFeature is
_registerFeatureFunction(this.batchCancelLimitOrders.selector);
_registerFeatureFunction(this.batchCancelRfqOrders.selector);
_registerFeatureFunction(this.cancelPairLimitOrders.selector);
_registerFeatureFunction(this.cancelPairLimitOrdersWithSigner.selector);
_registerFeatureFunction(this.batchCancelPairLimitOrders.selector);
_registerFeatureFunction(this.batchCancelPairLimitOrdersWithSigner.selector);
_registerFeatureFunction(this.cancelPairRfqOrders.selector);
_registerFeatureFunction(this.cancelPairRfqOrdersWithSigner.selector);
_registerFeatureFunction(this.batchCancelPairRfqOrders.selector);
_registerFeatureFunction(this.batchCancelPairRfqOrdersWithSigner.selector);
_registerFeatureFunction(this.getLimitOrderInfo.selector);
_registerFeatureFunction(this.getRfqOrderInfo.selector);
_registerFeatureFunction(this.getLimitOrderHash.selector);
@@ -87,6 +91,8 @@ contract NativeOrdersFeature is
_registerFeatureFunction(this.getRfqOrderRelevantState.selector);
_registerFeatureFunction(this.batchGetLimitOrderRelevantStates.selector);
_registerFeatureFunction(this.batchGetRfqOrderRelevantStates.selector);
_registerFeatureFunction(this.registerAllowedOrderSigner.selector);
_registerFeatureFunction(this.isValidOrderSigner.selector);
return LibMigrate.MIGRATE_SUCCESS;
}
}

View File

@@ -113,4 +113,14 @@ interface INativeOrdersEvents {
address[] addrs,
bool allowed
);
/// @dev Emitted when new order signers are registered
/// @param maker The maker address that is registering a designated signer.
/// @param signer The address that will sign on behalf of maker.
/// @param allowed Indicates whether the address should be allowed.
event OrderSignerRegistered(
address maker,
address signer,
bool allowed
);
}

View File

@@ -137,13 +137,13 @@ interface INativeOrdersFeature is
external
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount);
/// @dev Cancel a single limit order. The caller must be the maker.
/// @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;
/// @dev Cancel a single RFQ order. The caller must be the maker.
/// @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)
@@ -156,13 +156,13 @@ interface INativeOrdersFeature is
function registerAllowedRfqOrigins(address[] memory origins, bool allowed)
external;
/// @dev Cancel multiple limit orders. The caller must be the maker.
/// @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;
/// @dev Cancel multiple RFQ orders. The caller must be the maker.
/// @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)
@@ -183,7 +183,23 @@ interface INativeOrdersFeature is
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
/// than the value provided. The caller must be a signer registered to the maker.
/// Subsequent calls to this function with the same maker and pair require the
/// new salt to be >= the old salt.
/// @param maker The maker for which to cancel.
/// @param makerToken The maker token.
/// @param takerToken The taker token.
/// @param minValidSalt The new minimum valid salt.
function cancelPairLimitOrdersWithSigner(
address maker,
IERC20TokenV06 makerToken,
IERC20TokenV06 takerToken,
uint256 minValidSalt
)
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
/// calls to this function with the same caller and pair require the
/// new salt to be >= the old salt.
/// @param makerTokens The maker tokens.
@@ -196,6 +212,22 @@ interface INativeOrdersFeature is
)
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.
/// Subsequent calls to this function with the same maker and pair require the
/// new salt to be >= the old salt.
/// @param maker The maker for which to cancel.
/// @param makerTokens The maker tokens.
/// @param takerTokens The taker tokens.
/// @param minValidSalts The new minimum valid salts.
function batchCancelPairLimitOrdersWithSigner(
address maker,
IERC20TokenV06[] memory makerTokens,
IERC20TokenV06[] memory takerTokens,
uint256[] memory minValidSalts
)
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
/// calls to this function with the same caller and pair require the
@@ -211,7 +243,23 @@ interface INativeOrdersFeature is
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
/// than the value provided. The caller must be a signer registered to the maker.
/// Subsequent calls to this function with the same maker and pair require the
/// new salt to be >= the old salt.
/// @param maker The maker for which to cancel.
/// @param makerToken The maker token.
/// @param takerToken The taker token.
/// @param minValidSalt The new minimum valid salt.
function cancelPairRfqOrdersWithSigner(
address maker,
IERC20TokenV06 makerToken,
IERC20TokenV06 takerToken,
uint256 minValidSalt
)
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
/// calls to this function with the same caller and pair require the
/// new salt to be >= the old salt.
/// @param makerTokens The maker tokens.
@@ -224,6 +272,22 @@ interface INativeOrdersFeature is
)
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.
/// Subsequent calls to this function with the same maker and pair require the
/// new salt to be >= the old salt.
/// @param maker The maker for which to cancel.
/// @param makerTokens The maker tokens.
/// @param takerTokens The taker tokens.
/// @param minValidSalts The new minimum valid salts.
function batchCancelPairRfqOrdersWithSigner(
address maker,
IERC20TokenV06[] memory makerTokens,
IERC20TokenV06[] memory takerTokens,
uint256[] memory minValidSalts
)
external;
/// @dev Get the order info for a limit order.
/// @param order The limit order.
/// @return orderInfo Info about the order.
@@ -345,4 +409,25 @@ interface INativeOrdersFeature is
uint128[] memory actualFillableTakerTokenAmounts,
bool[] memory isSignatureValids
);
/// @dev Register a signer who can sign on behalf of msg.sender
/// 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;
/// @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);
}

View File

@@ -48,14 +48,14 @@ abstract contract NativeOrdersCancellation is
// solhint-disable no-empty-blocks
}
/// @dev Cancel a single limit order. The caller must be the maker.
/// @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 memory order)
public
{
bytes32 orderHash = getLimitOrderHash(order);
if (msg.sender != order.maker) {
if (msg.sender != order.maker && !isValidOrderSigner(order.maker, msg.sender)) {
LibNativeOrdersRichErrors.OnlyOrderMakerAllowed(
orderHash,
msg.sender,
@@ -65,14 +65,14 @@ abstract contract NativeOrdersCancellation is
_cancelOrderHash(orderHash, order.maker);
}
/// @dev Cancel a single RFQ order. The caller must be the maker.
/// @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 memory order)
public
{
bytes32 orderHash = getRfqOrderHash(order);
if (msg.sender != order.maker) {
if (msg.sender != order.maker && !isValidOrderSigner(order.maker, msg.sender)) {
LibNativeOrdersRichErrors.OnlyOrderMakerAllowed(
orderHash,
msg.sender,
@@ -82,7 +82,7 @@ abstract contract NativeOrdersCancellation is
_cancelOrderHash(orderHash, order.maker);
}
/// @dev Cancel multiple limit orders. The caller must be the maker.
/// @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[] memory orders)
@@ -93,7 +93,7 @@ abstract contract NativeOrdersCancellation is
}
}
/// @dev Cancel multiple RFQ orders. The caller must be the maker.
/// @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[] memory orders)
@@ -118,33 +118,34 @@ abstract contract NativeOrdersCancellation is
)
public
{
LibNativeOrdersStorage.Storage storage stor =
LibNativeOrdersStorage.getStorage();
_cancelPairLimitOrders(msg.sender, makerToken, takerToken, minValidSalt);
}
uint256 oldMinValidSalt =
stor.limitOrdersMakerToMakerTokenToTakerTokenToMinValidOrderSalt
[msg.sender]
[address(makerToken)]
[address(takerToken)];
// New min salt must >= the old one.
if (oldMinValidSalt > minValidSalt) {
LibNativeOrdersRichErrors.
CancelSaltTooLowError(minValidSalt, oldMinValidSalt)
.rrevert();
/// @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.
/// Subsequent calls to this function with the same caller and pair require the
/// new salt to be >= the old salt.
/// @param maker the maker for whom the msg.sender is the signer.
/// @param makerToken The maker token.
/// @param takerToken The taker token.
/// @param minValidSalt The new minimum valid salt.
function cancelPairLimitOrdersWithSigner(
address maker,
IERC20TokenV06 makerToken,
IERC20TokenV06 takerToken,
uint256 minValidSalt
)
public
{
// verify that the signer is authorized for the maker
if (!isValidOrderSigner(maker, msg.sender)) {
LibNativeOrdersRichErrors.InvalidSignerError(
maker,
msg.sender
).rrevert();
}
stor.limitOrdersMakerToMakerTokenToTakerTokenToMinValidOrderSalt
[msg.sender]
[address(makerToken)]
[address(takerToken)] = minValidSalt;
emit PairCancelledLimitOrders(
msg.sender,
address(makerToken),
address(takerToken),
minValidSalt
);
_cancelPairLimitOrders(maker, makerToken, takerToken, minValidSalt);
}
/// @dev Cancel all limit orders for a given maker and pair with a salt less
@@ -168,7 +169,47 @@ abstract contract NativeOrdersCancellation is
);
for (uint256 i = 0; i < makerTokens.length; ++i) {
cancelPairLimitOrders(
_cancelPairLimitOrders(
msg.sender,
makerTokens[i],
takerTokens[i],
minValidSalts[i]
);
}
}
/// @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.
/// Subsequent calls to this function with the same caller and pair require the
/// new salt to be >= the old salt.
/// @param maker the maker for whom the msg.sender is the signer.
/// @param makerTokens The maker tokens.
/// @param takerTokens The taker tokens.
/// @param minValidSalts The new minimum valid salts.
function batchCancelPairLimitOrdersWithSigner(
address maker,
IERC20TokenV06[] memory makerTokens,
IERC20TokenV06[] memory takerTokens,
uint256[] memory minValidSalts
)
public
{
require(
makerTokens.length == takerTokens.length &&
makerTokens.length == minValidSalts.length,
"NativeOrdersFeature/MISMATCHED_PAIR_ORDERS_ARRAY_LENGTHS"
);
if (!isValidOrderSigner(maker, msg.sender)) {
LibNativeOrdersRichErrors.InvalidSignerError(
maker,
msg.sender
).rrevert();
}
for (uint256 i = 0; i < makerTokens.length; ++i) {
_cancelPairLimitOrders(
maker,
makerTokens[i],
takerTokens[i],
minValidSalts[i]
@@ -190,33 +231,33 @@ abstract contract NativeOrdersCancellation is
)
public
{
LibNativeOrdersStorage.Storage storage stor =
LibNativeOrdersStorage.getStorage();
_cancelPairRfqOrders(msg.sender, makerToken, takerToken, minValidSalt);
}
uint256 oldMinValidSalt =
stor.rfqOrdersMakerToMakerTokenToTakerTokenToMinValidOrderSalt
[msg.sender]
[address(makerToken)]
[address(takerToken)];
// New min salt must >= the old one.
if (oldMinValidSalt > minValidSalt) {
LibNativeOrdersRichErrors.
CancelSaltTooLowError(minValidSalt, oldMinValidSalt)
.rrevert();
/// @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.
/// Subsequent calls to this function with the same caller and pair require the
/// new salt to be >= the old salt.
/// @param maker the maker for whom the msg.sender is the signer.
/// @param makerToken The maker token.
/// @param takerToken The taker token.
/// @param minValidSalt The new minimum valid salt.
function cancelPairRfqOrdersWithSigner(
address maker,
IERC20TokenV06 makerToken,
IERC20TokenV06 takerToken,
uint256 minValidSalt
)
public
{
if (!isValidOrderSigner(maker, msg.sender)) {
LibNativeOrdersRichErrors.InvalidSignerError(
maker,
msg.sender
).rrevert();
}
stor.rfqOrdersMakerToMakerTokenToTakerTokenToMinValidOrderSalt
[msg.sender]
[address(makerToken)]
[address(takerToken)] = minValidSalt;
emit PairCancelledRfqOrders(
msg.sender,
address(makerToken),
address(takerToken),
minValidSalt
);
_cancelPairRfqOrders(maker, makerToken, takerToken, minValidSalt);
}
/// @dev Cancel all RFQ orders for a given maker and pair with a salt less
@@ -240,7 +281,47 @@ abstract contract NativeOrdersCancellation is
);
for (uint256 i = 0; i < makerTokens.length; ++i) {
cancelPairRfqOrders(
_cancelPairRfqOrders(
msg.sender,
makerTokens[i],
takerTokens[i],
minValidSalts[i]
);
}
}
/// @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.
/// Subsequent calls to this function with the same caller and pair require the
/// new salt to be >= the old salt.
/// @param maker the maker for whom the msg.sender is the signer.
/// @param makerTokens The maker tokens.
/// @param takerTokens The taker tokens.
/// @param minValidSalts The new minimum valid salts.
function batchCancelPairRfqOrdersWithSigner(
address maker,
IERC20TokenV06[] memory makerTokens,
IERC20TokenV06[] memory takerTokens,
uint256[] memory minValidSalts
)
public
{
require(
makerTokens.length == takerTokens.length &&
makerTokens.length == minValidSalts.length,
"NativeOrdersFeature/MISMATCHED_PAIR_ORDERS_ARRAY_LENGTHS"
);
if (!isValidOrderSigner(maker, msg.sender)) {
LibNativeOrdersRichErrors.InvalidSignerError(
maker,
msg.sender
).rrevert();
}
for (uint256 i = 0; i < makerTokens.length; ++i) {
_cancelPairRfqOrders(
maker,
makerTokens[i],
takerTokens[i],
minValidSalts[i]
@@ -262,4 +343,90 @@ abstract contract NativeOrdersCancellation is
emit OrderCancelled(orderHash, maker);
}
/// @dev Cancel all RFQ orders for a given maker and pair with a salt less
/// than the value provided.
/// @param maker The target maker address
/// @param makerToken The maker token.
/// @param takerToken The taker token.
/// @param minValidSalt The new minimum valid salt.
function _cancelPairRfqOrders(
address maker,
IERC20TokenV06 makerToken,
IERC20TokenV06 takerToken,
uint256 minValidSalt
)
private
{
LibNativeOrdersStorage.Storage storage stor =
LibNativeOrdersStorage.getStorage();
uint256 oldMinValidSalt =
stor.rfqOrdersMakerToMakerTokenToTakerTokenToMinValidOrderSalt
[maker]
[address(makerToken)]
[address(takerToken)];
// New min salt must >= the old one.
if (oldMinValidSalt > minValidSalt) {
LibNativeOrdersRichErrors.
CancelSaltTooLowError(minValidSalt, oldMinValidSalt)
.rrevert();
}
stor.rfqOrdersMakerToMakerTokenToTakerTokenToMinValidOrderSalt
[maker]
[address(makerToken)]
[address(takerToken)] = minValidSalt;
emit PairCancelledRfqOrders(
maker,
address(makerToken),
address(takerToken),
minValidSalt
);
}
/// @dev Cancel all limit orders for a given maker and pair with a salt less
/// than the value provided.
/// @param maker The target maker address
/// @param makerToken The maker token.
/// @param takerToken The taker token.
/// @param minValidSalt The new minimum valid salt.
function _cancelPairLimitOrders(
address maker,
IERC20TokenV06 makerToken,
IERC20TokenV06 takerToken,
uint256 minValidSalt
)
private
{
LibNativeOrdersStorage.Storage storage stor =
LibNativeOrdersStorage.getStorage();
uint256 oldMinValidSalt =
stor.limitOrdersMakerToMakerTokenToTakerTokenToMinValidOrderSalt
[maker]
[address(makerToken)]
[address(takerToken)];
// New min salt must >= the old one.
if (oldMinValidSalt > minValidSalt) {
LibNativeOrdersRichErrors.
CancelSaltTooLowError(minValidSalt, oldMinValidSalt)
.rrevert();
}
stor.limitOrdersMakerToMakerTokenToTakerTokenToMinValidOrderSalt
[maker]
[address(makerToken)]
[address(takerToken)] = minValidSalt;
emit PairCancelledLimitOrders(
maker,
address(makerToken),
address(takerToken),
minValidSalt
);
}
}

View File

@@ -168,8 +168,10 @@ abstract contract NativeOrdersInfo is
orderInfo: orderInfo
})
);
isSignatureValid = order.maker ==
LibSignature.getSignerOfHash(orderInfo.orderHash, signature);
address signerOfHash = LibSignature.getSignerOfHash(orderInfo.orderHash, signature);
isSignatureValid =
(order.maker == signerOfHash) ||
isValidOrderSigner(order.maker, signerOfHash);
}
/// @dev Get order info, fillable amount, and signature validity for an RFQ order.
@@ -202,8 +204,10 @@ abstract contract NativeOrdersInfo is
orderInfo: orderInfo
})
);
isSignatureValid = order.maker ==
LibSignature.getSignerOfHash(orderInfo.orderHash, signature);
address signerOfHash = LibSignature.getSignerOfHash(orderInfo.orderHash, signature);
isSignatureValid =
(order.maker == signerOfHash) ||
isValidOrderSigner(order.maker, signerOfHash);
}
/// @dev Batch version of `getLimitOrderRelevantState()`, without reverting.
@@ -389,4 +393,22 @@ abstract contract NativeOrdersInfo is
uint256(params.orderTakerAmount)
).safeDowncastToUint128();
}
/// @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
)
public
view
returns (bool isValid)
{
// returns false if it the mapping doesn't exist
return LibNativeOrdersStorage.getStorage()
.orderSignerRegistry
[maker]
[signer];
}
}

View File

@@ -370,7 +370,7 @@ abstract contract NativeOrdersSettlement is
orderInfo.orderHash,
params.signature
);
if (signer != params.order.maker) {
if (signer != params.order.maker && !isValidOrderSigner(params.order.maker, signer)) {
LibNativeOrdersRichErrors.OrderNotSignedByMakerError(
orderInfo.orderHash,
signer,
@@ -478,7 +478,7 @@ abstract contract NativeOrdersSettlement is
// Signature must be valid for the order.
{
address signer = LibSignature.getSignerOfHash(orderInfo.orderHash, signature);
if (signer != order.maker) {
if (signer != order.maker && !isValidOrderSigner(order.maker, signer)) {
LibNativeOrdersRichErrors.OrderNotSignedByMakerError(
orderInfo.orderHash,
signer,
@@ -565,4 +565,21 @@ abstract contract NativeOrdersSettlement is
makerTokenFilledAmount
);
}
/// @dev register a signer who can sign on behalf of msg.sender
/// @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
{
LibNativeOrdersStorage.Storage storage stor =
LibNativeOrdersStorage.getStorage();
stor.orderSignerRegistry[msg.sender][signer] = allowed;
emit OrderSignerRegistered(msg.sender, signer, allowed);
}
}

View File

@@ -43,6 +43,9 @@ library LibNativeOrdersStorage {
// For a given order origin, which tx.origin addresses are allowed to
// fill the order.
mapping(address => mapping(address => bool)) originRegistry;
// For a given maker address, which addresses are allowed to
// sign on its behalf.
mapping(address => mapping(address => bool)) orderSignerRegistry;
}
/// @dev Get the storage bucket for this contract.

View File

@@ -0,0 +1,55 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2021 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-utils/contracts/src/v06/OwnableV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "../src/IZeroEx.sol";
contract TestOrderSignerRegistryWithContractWallet is OwnableV06 {
IZeroEx immutable zeroex;
constructor(IZeroEx _zeroex) public {
zeroex = _zeroex;
}
function registerAllowedOrderSigner(
address signer,
bool allowed
)
external
onlyOwner
{
zeroex.registerAllowedOrderSigner(signer, allowed);
}
function approveERC20(
IERC20TokenV06 token,
address spender,
uint256 value
)
external
onlyOwner
{
token.approve(spender, value);
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-zero-ex",
"version": "0.21.1",
"version": "0.22.0",
"engines": {
"node": ">=6.12"
},
@@ -43,7 +43,7 @@
"config": {
"publicInterfaceContracts": "IZeroEx,ZeroEx,FullMigration,InitialMigration,IFlashWallet,IERC20Transformer,IOwnableFeature,ISimpleFunctionRegistryFeature,ITransformERC20Feature,FillQuoteTransformer,PayTakerTransformer,PositiveSlippageFeeTransformer,WethTransformer,OwnableFeature,SimpleFunctionRegistryFeature,TransformERC20Feature,AffiliateFeeTransformer,MetaTransactionsFeature,LogMetadataTransformer,BridgeAdapter,LiquidityProviderFeature,ILiquidityProviderFeature,NativeOrdersFeature,INativeOrdersFeature,FeeCollectorController,FeeCollector,CurveLiquidityProvider,BatchFillNativeOrdersFeature,IBatchFillNativeOrdersFeature,MultiplexFeature,IMultiplexFeature",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
"abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|BatchFillNativeOrdersFeature|BootstrapFeature|BridgeAdapter|BridgeProtocols|CurveLiquidityProvider|FeeCollector|FeeCollectorController|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinProtocolFees|FixinReentrancyGuard|FixinTokenSpender|FlashWallet|FullMigration|IBatchFillNativeOrdersFeature|IBootstrapFeature|IBridgeAdapter|IERC20Bridge|IERC20Transformer|IFeature|IFlashWallet|ILiquidityProvider|ILiquidityProviderFeature|ILiquidityProviderSandbox|IMetaTransactionsFeature|IMooniswapPool|IMultiplexFeature|INativeOrdersEvents|INativeOrdersFeature|IOwnableFeature|IPancakeSwapFeature|ISimpleFunctionRegistryFeature|IStaking|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IUniswapV2Pair|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC20Transformer|LibFeeCollector|LibLiquidityProviderRichErrors|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibNativeOrder|LibNativeOrdersRichErrors|LibNativeOrdersStorage|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignature|LibSignatureRichErrors|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LiquidityProviderFeature|LiquidityProviderSandbox|LogMetadataTransformer|MetaTransactionsFeature|MixinBalancer|MixinBancor|MixinCoFiX|MixinCryptoCom|MixinCurve|MixinDodo|MixinDodoV2|MixinKyber|MixinMStable|MixinMakerPSM|MixinMooniswap|MixinNerve|MixinOasis|MixinShell|MixinUniswap|MixinUniswapV2|MixinZeroExBridge|MooniswapLiquidityProvider|MultiplexFeature|NativeOrdersCancellation|NativeOrdersFeature|NativeOrdersInfo|NativeOrdersProtocolFees|NativeOrdersSettlement|OwnableFeature|PancakeSwapFeature|PayTakerTransformer|PermissionlessTransformerDeployer|PositiveSlippageFeeTransformer|SimpleFunctionRegistryFeature|TestBridge|TestCallTarget|TestCurve|TestDelegateCaller|TestFeeCollectorController|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFixinProtocolFees|TestFixinTokenSpender|TestFullMigration|TestInitialMigration|TestLibNativeOrder|TestLibSignature|TestLiquidityProvider|TestMetaTransactionsNativeOrdersFeature|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC20Token|TestMooniswap|TestNativeOrdersFeature|TestPermissionlessTransformerDeployerSuicidal|TestPermissionlessTransformerDeployerTransformer|TestRfqOriginRegistration|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestStaking|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestWeth|TestWethTransformerHost|TestZeroExFeature|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|WethTransformer|ZeroEx|ZeroExOptimized).json"
"abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|BatchFillNativeOrdersFeature|BootstrapFeature|BridgeAdapter|BridgeProtocols|CurveLiquidityProvider|FeeCollector|FeeCollectorController|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinProtocolFees|FixinReentrancyGuard|FixinTokenSpender|FlashWallet|FullMigration|IBatchFillNativeOrdersFeature|IBootstrapFeature|IBridgeAdapter|IERC20Bridge|IERC20Transformer|IFeature|IFlashWallet|ILiquidityProvider|ILiquidityProviderFeature|ILiquidityProviderSandbox|IMetaTransactionsFeature|IMooniswapPool|IMultiplexFeature|INativeOrdersEvents|INativeOrdersFeature|IOwnableFeature|IPancakeSwapFeature|ISimpleFunctionRegistryFeature|IStaking|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IUniswapV2Pair|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC20Transformer|LibFeeCollector|LibLiquidityProviderRichErrors|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibNativeOrder|LibNativeOrdersRichErrors|LibNativeOrdersStorage|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignature|LibSignatureRichErrors|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LiquidityProviderFeature|LiquidityProviderSandbox|LogMetadataTransformer|MetaTransactionsFeature|MixinBalancer|MixinBancor|MixinCoFiX|MixinCryptoCom|MixinCurve|MixinDodo|MixinDodoV2|MixinKyber|MixinMStable|MixinMakerPSM|MixinMooniswap|MixinNerve|MixinOasis|MixinShell|MixinUniswap|MixinUniswapV2|MixinZeroExBridge|MooniswapLiquidityProvider|MultiplexFeature|NativeOrdersCancellation|NativeOrdersFeature|NativeOrdersInfo|NativeOrdersProtocolFees|NativeOrdersSettlement|OwnableFeature|PancakeSwapFeature|PayTakerTransformer|PermissionlessTransformerDeployer|PositiveSlippageFeeTransformer|SimpleFunctionRegistryFeature|TestBridge|TestCallTarget|TestCurve|TestDelegateCaller|TestFeeCollectorController|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFixinProtocolFees|TestFixinTokenSpender|TestFullMigration|TestInitialMigration|TestLibNativeOrder|TestLibSignature|TestLiquidityProvider|TestMetaTransactionsNativeOrdersFeature|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC20Token|TestMooniswap|TestNativeOrdersFeature|TestOrderSignerRegistryWithContractWallet|TestPermissionlessTransformerDeployerSuicidal|TestPermissionlessTransformerDeployerTransformer|TestRfqOriginRegistration|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestStaking|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestWeth|TestWethTransformerHost|TestZeroExFeature|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|WethTransformer|ZeroEx|ZeroExOptimized).json"
},
"repository": {
"type": "git",
@@ -83,7 +83,7 @@
},
"dependencies": {
"@0x/base-contract": "^6.2.18",
"@0x/protocol-utils": "^1.4.1",
"@0x/protocol-utils": "^1.5.0",
"@0x/subproviders": "^6.4.1",
"@0x/types": "^3.3.1",
"@0x/typescript-typings": "^5.1.6",

View File

@@ -127,6 +127,7 @@ import * as TestMintableERC20Token from '../test/generated-artifacts/TestMintabl
import * as TestMintTokenERC20Transformer from '../test/generated-artifacts/TestMintTokenERC20Transformer.json';
import * as TestMooniswap from '../test/generated-artifacts/TestMooniswap.json';
import * as TestNativeOrdersFeature from '../test/generated-artifacts/TestNativeOrdersFeature.json';
import * as TestOrderSignerRegistryWithContractWallet from '../test/generated-artifacts/TestOrderSignerRegistryWithContractWallet.json';
import * as TestPermissionlessTransformerDeployerSuicidal from '../test/generated-artifacts/TestPermissionlessTransformerDeployerSuicidal.json';
import * as TestPermissionlessTransformerDeployerTransformer from '../test/generated-artifacts/TestPermissionlessTransformerDeployerTransformer.json';
import * as TestRfqOriginRegistration from '../test/generated-artifacts/TestRfqOriginRegistration.json';
@@ -278,6 +279,7 @@ export const artifacts = {
TestMintableERC20Token: TestMintableERC20Token as ContractArtifact,
TestMooniswap: TestMooniswap as ContractArtifact,
TestNativeOrdersFeature: TestNativeOrdersFeature as ContractArtifact,
TestOrderSignerRegistryWithContractWallet: TestOrderSignerRegistryWithContractWallet as ContractArtifact,
TestPermissionlessTransformerDeployerSuicidal: TestPermissionlessTransformerDeployerSuicidal as ContractArtifact,
TestPermissionlessTransformerDeployerTransformer: TestPermissionlessTransformerDeployerTransformer as ContractArtifact,
TestRfqOriginRegistration: TestRfqOriginRegistration as ContractArtifact,

View File

@@ -7,7 +7,15 @@ import {
randomAddress,
verifyEventsFromLogs,
} from '@0x/contracts-test-utils';
import { LimitOrder, LimitOrderFields, OrderStatus, RevertErrors, RfqOrder, RfqOrderFields } from '@0x/protocol-utils';
import {
LimitOrder,
LimitOrderFields,
OrderStatus,
RevertErrors,
RfqOrder,
RfqOrderFields,
SignatureType,
} from '@0x/protocol-utils';
import { AnyRevertError, BigNumber } from '@0x/utils';
import { TransactionReceiptWithDecodedLogs } from 'ethereum-types';
@@ -25,7 +33,11 @@ import {
getRandomRfqOrder,
NativeOrdersTestEnvironment,
} from '../utils/orders';
import { TestMintableERC20TokenContract, TestRfqOriginRegistrationContract } from '../wrappers';
import {
TestMintableERC20TokenContract,
TestOrderSignerRegistryWithContractWalletContract,
TestRfqOriginRegistrationContract,
} from '../wrappers';
blockchainTests.resets('NativeOrdersFeature', env => {
const { NULL_ADDRESS, MAX_UINT256, NULL_BYTES32, ZERO_AMOUNT } = constants;
@@ -36,17 +48,28 @@ blockchainTests.resets('NativeOrdersFeature', env => {
let taker: string;
let notMaker: string;
let notTaker: string;
let contractWalletOwner: string;
let contractWalletSigner: string;
let zeroEx: IZeroExContract;
let verifyingContract: string;
let makerToken: TestMintableERC20TokenContract;
let takerToken: TestMintableERC20TokenContract;
let wethToken: TestMintableERC20TokenContract;
let testRfqOriginRegistration: TestRfqOriginRegistrationContract;
let contractWallet: TestOrderSignerRegistryWithContractWalletContract;
let testUtils: NativeOrdersTestEnvironment;
before(async () => {
let owner;
[owner, maker, taker, notMaker, notTaker] = await env.getAccountAddressesAsync();
[
owner,
maker,
taker,
notMaker,
notTaker,
contractWalletOwner,
contractWalletSigner,
] = await env.getAccountAddressesAsync();
[makerToken, takerToken, wethToken] = await Promise.all(
[...new Array(3)].map(async () =>
TestMintableERC20TokenContract.deployFrom0xArtifactAsync(
@@ -82,6 +105,21 @@ blockchainTests.resets('NativeOrdersFeature', env => {
env.txDefaults,
artifacts,
);
// contract wallet for signer delegation
contractWallet = await TestOrderSignerRegistryWithContractWalletContract.deployFrom0xArtifactAsync(
artifacts.TestOrderSignerRegistryWithContractWallet,
env.provider,
{
from: contractWalletOwner,
},
artifacts,
zeroEx.address,
);
await contractWallet
.approveERC20(makerToken.address, zeroEx.address, MAX_UINT256)
.awaitTransactionSuccessAsync({ from: contractWalletOwner });
testUtils = new NativeOrdersTestEnvironment(
maker,
taker,
@@ -1569,4 +1607,432 @@ blockchainTests.resets('NativeOrdersFeature', env => {
}
});
});
describe('registerAllowedSigner()', () => {
it('fires appropriate events', async () => {
const receiptAllow = await contractWallet
.registerAllowedOrderSigner(contractWalletSigner, true)
.awaitTransactionSuccessAsync({ from: contractWalletOwner });
verifyEventsFromLogs(
receiptAllow.logs,
[
{
maker: contractWallet.address,
signer: contractWalletSigner,
allowed: true,
},
],
IZeroExEvents.OrderSignerRegistered,
);
// then disallow signer
const receiptDisallow = await contractWallet
.registerAllowedOrderSigner(contractWalletSigner, false)
.awaitTransactionSuccessAsync({ from: contractWalletOwner });
verifyEventsFromLogs(
receiptDisallow.logs,
[
{
maker: contractWallet.address,
signer: contractWalletSigner,
allowed: false,
},
],
IZeroExEvents.OrderSignerRegistered,
);
});
it('allows for fills on orders signed by a approved signer', async () => {
const order = getTestRfqOrder({ maker: contractWallet.address });
const sig = await order.getSignatureWithProviderAsync(
env.provider,
SignatureType.EthSign,
contractWalletSigner,
);
// covers taker
await testUtils.prepareBalancesForOrdersAsync([order]);
// need to provide contract wallet with a balance
await makerToken.mint(contractWallet.address, order.makerAmount).awaitTransactionSuccessAsync();
await contractWallet
.registerAllowedOrderSigner(contractWalletSigner, true)
.awaitTransactionSuccessAsync({ from: contractWalletOwner });
await zeroEx.fillRfqOrder(order, sig, order.takerAmount).awaitTransactionSuccessAsync({ from: taker });
const info = await zeroEx.getRfqOrderInfo(order).callAsync();
assertOrderInfoEquals(info, {
status: OrderStatus.Filled,
orderHash: order.getHash(),
takerTokenFilledAmount: order.takerAmount,
});
});
it('disallows fills if the signer is revoked', async () => {
const order = getTestRfqOrder({ maker: contractWallet.address });
const sig = await order.getSignatureWithProviderAsync(
env.provider,
SignatureType.EthSign,
contractWalletSigner,
);
// covers taker
await testUtils.prepareBalancesForOrdersAsync([order]);
// need to provide contract wallet with a balance
await makerToken.mint(contractWallet.address, order.makerAmount).awaitTransactionSuccessAsync();
// first allow signer
await contractWallet
.registerAllowedOrderSigner(contractWalletSigner, true)
.awaitTransactionSuccessAsync({ from: contractWalletOwner });
// then disallow signer
await contractWallet
.registerAllowedOrderSigner(contractWalletSigner, false)
.awaitTransactionSuccessAsync({ from: contractWalletOwner });
const tx = zeroEx.fillRfqOrder(order, sig, order.takerAmount).awaitTransactionSuccessAsync({ from: taker });
return expect(tx).to.revertWith(
new RevertErrors.NativeOrders.OrderNotSignedByMakerError(
order.getHash(),
contractWalletSigner,
order.maker,
),
);
});
it(`doesn't allow fills with an unapproved signer`, async () => {
const order = getTestRfqOrder({ maker: contractWallet.address });
const sig = await order.getSignatureWithProviderAsync(env.provider, SignatureType.EthSign, maker);
// covers taker
await testUtils.prepareBalancesForOrdersAsync([order]);
// need to provide contract wallet with a balance
await makerToken.mint(contractWallet.address, order.makerAmount).awaitTransactionSuccessAsync();
const tx = zeroEx.fillRfqOrder(order, sig, order.takerAmount).awaitTransactionSuccessAsync({ from: taker });
return expect(tx).to.revertWith(
new RevertErrors.NativeOrders.OrderNotSignedByMakerError(order.getHash(), maker, order.maker),
);
});
it(`allows an approved signer to cancel an RFQ order`, async () => {
const order = getTestRfqOrder({ maker: contractWallet.address });
await contractWallet
.registerAllowedOrderSigner(contractWalletSigner, true)
.awaitTransactionSuccessAsync({ from: contractWalletOwner });
const receipt = await zeroEx
.cancelRfqOrder(order)
.awaitTransactionSuccessAsync({ from: contractWalletSigner });
verifyEventsFromLogs(
receipt.logs,
[{ maker: contractWallet.address, orderHash: order.getHash() }],
IZeroExEvents.OrderCancelled,
);
const info = await zeroEx.getRfqOrderInfo(order).callAsync();
assertOrderInfoEquals(info, {
status: OrderStatus.Cancelled,
orderHash: order.getHash(),
takerTokenFilledAmount: new BigNumber(0),
});
});
it(`allows an approved signer to cancel a limit order`, async () => {
const order = getTestLimitOrder({ maker: contractWallet.address });
await contractWallet
.registerAllowedOrderSigner(contractWalletSigner, true)
.awaitTransactionSuccessAsync({ from: contractWalletOwner });
const receipt = await zeroEx
.cancelLimitOrder(order)
.awaitTransactionSuccessAsync({ from: contractWalletSigner });
verifyEventsFromLogs(
receipt.logs,
[{ maker: contractWallet.address, orderHash: order.getHash() }],
IZeroExEvents.OrderCancelled,
);
const info = await zeroEx.getLimitOrderInfo(order).callAsync();
assertOrderInfoEquals(info, {
status: OrderStatus.Cancelled,
orderHash: order.getHash(),
takerTokenFilledAmount: new BigNumber(0),
});
});
it(`doesn't allow an unapproved signer to cancel an RFQ order`, async () => {
const order = getTestRfqOrder({ maker: contractWallet.address });
const tx = zeroEx.cancelRfqOrder(order).awaitTransactionSuccessAsync({ from: maker });
return expect(tx).to.revertWith(
new RevertErrors.NativeOrders.OnlyOrderMakerAllowed(order.getHash(), maker, order.maker),
);
});
it(`doesn't allow an unapproved signer to cancel a limit order`, async () => {
const order = getTestLimitOrder({ maker: contractWallet.address });
const tx = zeroEx.cancelLimitOrder(order).awaitTransactionSuccessAsync({ from: maker });
return expect(tx).to.revertWith(
new RevertErrors.NativeOrders.OnlyOrderMakerAllowed(order.getHash(), maker, order.maker),
);
});
it(`allows a signer to cancel pair RFQ orders`, async () => {
const order = getTestRfqOrder({ maker: contractWallet.address, salt: new BigNumber(1) });
await contractWallet
.registerAllowedOrderSigner(contractWalletSigner, true)
.awaitTransactionSuccessAsync({ from: contractWalletOwner });
// Cancel salts <= the order's
const minValidSalt = order.salt.plus(1);
const receipt = await zeroEx
.cancelPairRfqOrdersWithSigner(
contractWallet.address,
makerToken.address,
takerToken.address,
minValidSalt,
)
.awaitTransactionSuccessAsync({ from: contractWalletSigner });
verifyEventsFromLogs(
receipt.logs,
[
{
maker: contractWallet.address,
makerToken: makerToken.address,
takerToken: takerToken.address,
minValidSalt,
},
],
IZeroExEvents.PairCancelledRfqOrders,
);
const info = await zeroEx.getRfqOrderInfo(order).callAsync();
assertOrderInfoEquals(info, {
status: OrderStatus.Cancelled,
orderHash: order.getHash(),
takerTokenFilledAmount: new BigNumber(0),
});
});
it(`doesn't allow an unapproved signer to cancel pair RFQ orders`, async () => {
const minValidSalt = new BigNumber(2);
const tx = zeroEx
.cancelPairRfqOrdersWithSigner(
contractWallet.address,
makerToken.address,
takerToken.address,
minValidSalt,
)
.awaitTransactionSuccessAsync({ from: maker });
return expect(tx).to.revertWith(
new RevertErrors.NativeOrders.InvalidSignerError(contractWallet.address, maker),
);
});
it(`allows a signer to cancel pair limit orders`, async () => {
const order = getTestLimitOrder({ maker: contractWallet.address, salt: new BigNumber(1) });
await contractWallet
.registerAllowedOrderSigner(contractWalletSigner, true)
.awaitTransactionSuccessAsync({ from: contractWalletOwner });
// Cancel salts <= the order's
const minValidSalt = order.salt.plus(1);
const receipt = await zeroEx
.cancelPairLimitOrdersWithSigner(
contractWallet.address,
makerToken.address,
takerToken.address,
minValidSalt,
)
.awaitTransactionSuccessAsync({ from: contractWalletSigner });
verifyEventsFromLogs(
receipt.logs,
[
{
maker: contractWallet.address,
makerToken: makerToken.address,
takerToken: takerToken.address,
minValidSalt,
},
],
IZeroExEvents.PairCancelledLimitOrders,
);
const info = await zeroEx.getLimitOrderInfo(order).callAsync();
assertOrderInfoEquals(info, {
status: OrderStatus.Cancelled,
orderHash: order.getHash(),
takerTokenFilledAmount: new BigNumber(0),
});
});
it(`doesn't allow an unapproved signer to cancel pair limit orders`, async () => {
const minValidSalt = new BigNumber(2);
const tx = zeroEx
.cancelPairLimitOrdersWithSigner(
contractWallet.address,
makerToken.address,
takerToken.address,
minValidSalt,
)
.awaitTransactionSuccessAsync({ from: maker });
return expect(tx).to.revertWith(
new RevertErrors.NativeOrders.InvalidSignerError(contractWallet.address, maker),
);
});
it(`allows a signer to cancel multiple RFQ order pairs`, async () => {
const orders = [
getTestRfqOrder({ maker: contractWallet.address, salt: new BigNumber(1) }),
// Flip the tokens for the other order.
getTestRfqOrder({
makerToken: takerToken.address,
takerToken: makerToken.address,
maker: contractWallet.address,
salt: new BigNumber(1),
}),
];
await contractWallet
.registerAllowedOrderSigner(contractWalletSigner, true)
.awaitTransactionSuccessAsync({ from: contractWalletOwner });
const minValidSalt = new BigNumber(2);
const receipt = await zeroEx
.batchCancelPairRfqOrdersWithSigner(
contractWallet.address,
[makerToken.address, takerToken.address],
[takerToken.address, makerToken.address],
[minValidSalt, minValidSalt],
)
.awaitTransactionSuccessAsync({ from: contractWalletSigner });
verifyEventsFromLogs(
receipt.logs,
[
{
maker: contractWallet.address,
makerToken: makerToken.address,
takerToken: takerToken.address,
minValidSalt,
},
{
maker: contractWallet.address,
makerToken: takerToken.address,
takerToken: makerToken.address,
minValidSalt,
},
],
IZeroExEvents.PairCancelledRfqOrders,
);
const statuses = (await Promise.all(orders.map(o => zeroEx.getRfqOrderInfo(o).callAsync()))).map(
oi => oi.status,
);
expect(statuses).to.deep.eq([OrderStatus.Cancelled, OrderStatus.Cancelled]);
});
it(`doesn't allow an unapproved signer to batch cancel pair rfq orders`, async () => {
const minValidSalt = new BigNumber(2);
const tx = zeroEx
.batchCancelPairRfqOrdersWithSigner(
contractWallet.address,
[makerToken.address, takerToken.address],
[takerToken.address, makerToken.address],
[minValidSalt, minValidSalt],
)
.awaitTransactionSuccessAsync({ from: maker });
return expect(tx).to.revertWith(
new RevertErrors.NativeOrders.InvalidSignerError(contractWallet.address, maker),
);
});
it(`allows a signer to cancel multiple limit order pairs`, async () => {
const orders = [
getTestLimitOrder({ maker: contractWallet.address, salt: new BigNumber(1) }),
// Flip the tokens for the other order.
getTestLimitOrder({
makerToken: takerToken.address,
takerToken: makerToken.address,
maker: contractWallet.address,
salt: new BigNumber(1),
}),
];
await contractWallet
.registerAllowedOrderSigner(contractWalletSigner, true)
.awaitTransactionSuccessAsync({ from: contractWalletOwner });
const minValidSalt = new BigNumber(2);
const receipt = await zeroEx
.batchCancelPairLimitOrdersWithSigner(
contractWallet.address,
[makerToken.address, takerToken.address],
[takerToken.address, makerToken.address],
[minValidSalt, minValidSalt],
)
.awaitTransactionSuccessAsync({ from: contractWalletSigner });
verifyEventsFromLogs(
receipt.logs,
[
{
maker: contractWallet.address,
makerToken: makerToken.address,
takerToken: takerToken.address,
minValidSalt,
},
{
maker: contractWallet.address,
makerToken: takerToken.address,
takerToken: makerToken.address,
minValidSalt,
},
],
IZeroExEvents.PairCancelledLimitOrders,
);
const statuses = (await Promise.all(orders.map(o => zeroEx.getLimitOrderInfo(o).callAsync()))).map(
oi => oi.status,
);
expect(statuses).to.deep.eq([OrderStatus.Cancelled, OrderStatus.Cancelled]);
});
it(`doesn't allow an unapproved signer to batch cancel pair limit orders`, async () => {
const minValidSalt = new BigNumber(2);
const tx = zeroEx
.batchCancelPairLimitOrdersWithSigner(
contractWallet.address,
[makerToken.address, takerToken.address],
[takerToken.address, makerToken.address],
[minValidSalt, minValidSalt],
)
.awaitTransactionSuccessAsync({ from: maker });
return expect(tx).to.revertWith(
new RevertErrors.NativeOrders.InvalidSignerError(contractWallet.address, maker),
);
});
});
});

View File

@@ -125,6 +125,7 @@ export * from '../test/generated-wrappers/test_mint_token_erc20_transformer';
export * from '../test/generated-wrappers/test_mintable_erc20_token';
export * from '../test/generated-wrappers/test_mooniswap';
export * from '../test/generated-wrappers/test_native_orders_feature';
export * from '../test/generated-wrappers/test_order_signer_registry_with_contract_wallet';
export * from '../test/generated-wrappers/test_permissionless_transformer_deployer_suicidal';
export * from '../test/generated-wrappers/test_permissionless_transformer_deployer_transformer';
export * from '../test/generated-wrappers/test_rfq_origin_registration';

View File

@@ -156,6 +156,7 @@
"test/generated-artifacts/TestMintableERC20Token.json",
"test/generated-artifacts/TestMooniswap.json",
"test/generated-artifacts/TestNativeOrdersFeature.json",
"test/generated-artifacts/TestOrderSignerRegistryWithContractWallet.json",
"test/generated-artifacts/TestPermissionlessTransformerDeployerSuicidal.json",
"test/generated-artifacts/TestPermissionlessTransformerDeployerTransformer.json",
"test/generated-artifacts/TestRfqOriginRegistration.json",

View File

@@ -53,6 +53,8 @@ illustrates how events are emitted when trading through the Exchange Proxy.
+-------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------+
| `RfqOrderOriginsAllowed`_ | Emitted when a tx.origin is added/removed for RFQ, via `registerAllowedRfqOrigins <./functions.html#registerallowedrfqorigins>`_ | ExchangeProxy |
+-------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------+
| `OrderSignerRegistered`_ | Emitted when an order signer is added/removed for a maker, via `registerAllowedOrderSigner <./functions.html#registerallowedordersigner>`_ | ExchangeProxy |
+-------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------+
| `TransformedERC20`_ | Emitted when an `ERC20 Transformation <../advanced/erc20_transformations.html>`_ completes. | ExchangeProxy |
+-------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------+
| `TransformerDeployerUpdated`_ | Emitted when the Transformer Deployer is upgraded. | ExchangeProxy |
@@ -227,7 +229,7 @@ PairCancelledLimitOrders
);
PairCancelledRfqOrders
------------------------
----------------------
.. code-block:: solidity
@@ -311,6 +313,21 @@ RfqOrderOriginsAllowed
bool allowed
);
OrderSignerRegistered
-------------------------
.. code-block:: solidity
/// @dev Emitted when new order signers are registered
/// @param maker The maker address that is registering a designated signer.
/// @param signer The address that will sign on behalf of maker.
/// @param allowed Indicates whether the address should be allowed.
event OrderSignerRegistered(
address maker,
address signer,
bool allowed
);
TransformedERC20
----------------

View File

@@ -4,54 +4,69 @@ Basic Functionality
Below is a catalog of basic Exchange functionality. For more advanced usage, like meta-transactions and dex aggregation, see the Advanced section.
+---------------------------------+--------------------------------------------------------------------------+
| **Limit Orders** | **Overview** |
+---------------------------------+--------------------------------------------------------------------------+
| `fillLimitOrder`_ | Fills a Limit Order up to the amount requested. |
+---------------------------------+--------------------------------------------------------------------------+
| `fillOrKillLimitOrder`_ | Fills exactly the amount requested or reverts. |
+---------------------------------+--------------------------------------------------------------------------+
| `cancelLimitOrder`_ | Cancels an order so that it can no longer be filled. |
+---------------------------------+--------------------------------------------------------------------------+
| `batchCancelLimitOrders`_ | A batch call to `cancelLimitOrder`. |
+---------------------------------+--------------------------------------------------------------------------+
| `cancelPairLimitOrders`_ | Cancels Limit orders in a specific market pair. |
| | Ex: Cancel all Limit Orders selling WETH for USDC. |
+---------------------------------+--------------------------------------------------------------------------+
| `batchCancelPairLimitOrders`_ | A batch call to `cancelPairLimitOrders`. |
+---------------------------------+--------------------------------------------------------------------------+
| `getLimitOrderInfo`_ | Returns the state of a given order. |
+---------------------------------+--------------------------------------------------------------------------+
| `getLimitOrderHash`_ | Returns the EIP-712 hash for an order. |
+---------------------------------+--------------------------------------------------------------------------+
| **RFQ Orders** | **Overview** |
+---------------------------------+--------------------------------------------------------------------------+
| `fillRfqOrder`_ | These are analogous to the above LimitOrder functions. |
+---------------------------------+ |
| `fillOrKillRfqOrder`_ | |
+---------------------------------+ |
| `cancelRfqOrder`_ | |
+---------------------------------+ |
| `batchCancelRfqOrders`_ | |
+---------------------------------+ |
| `cancelPairRfqOrders`_ | |
+---------------------------------+ |
| `batchCancelPairRfqOrders`_ | |
+---------------------------------+ |
| `getRfqOrderInfo`_ | |
+---------------------------------+ |
| `getRfqOrderHash`_ | |
+---------------------------------+--------------------------------------------------------------------------+
| `registerAllowedRfqOrigins`_ | Register tx.origin addresses that are allowed to fill an RFQ order. |
+---------------------------------+--------------------------------------------------------------------------+
| **Protocol Fees** | **Overview** |
+---------------------------------+--------------------------------------------------------------------------+
| `getProtocolFeeMultiplier`_ | Takers of limit orders pay a protocol fee of `Multiplier * tx.gasprice`. |
| | This returns the `Multiplier`. |
+---------------------------------+--------------------------------------------------------------------------+
| `transferProtocolFeesForPools`_ | Transfers protocol fees from escrow to the 0x Staking System. |
| | This should be called near the end of each epoch. |
+---------------------------------+--------------------------------------------------------------------------+
+-----------------------------------------+--------------------------------------------------------------------------+
| **Limit Orders** | **Overview** |
+-----------------------------------------+--------------------------------------------------------------------------+
| `fillLimitOrder`_ | Fills a Limit Order up to the amount requested. |
+-----------------------------------------+--------------------------------------------------------------------------+
| `fillOrKillLimitOrder`_ | Fills exactly the amount requested or reverts. |
+-----------------------------------------+--------------------------------------------------------------------------+
| `cancelLimitOrder`_ | Cancels an order so that it can no longer be filled. |
+-----------------------------------------+--------------------------------------------------------------------------+
| `batchCancelLimitOrders`_ | A batch call to `cancelLimitOrder`. |
+-----------------------------------------+--------------------------------------------------------------------------+
| `cancelPairLimitOrders`_ | Cancels Limit orders in a specific market pair. |
| | Ex: Cancel all Limit Orders selling WETH for USDC. |
+-----------------------------------------+--------------------------------------------------------------------------+
| `cancelPairLimitOrdersWithSigner`_ | Same functionality to ``cancelPairLimitOrders`` but called by a |
| | registered order signer instead of the maker itself. |
+-----------------------------------------+--------------------------------------------------------------------------+
| `batchCancelPairLimitOrders`_ | A batch call to `cancelPairLimitOrders`. |
+-----------------------------------------+--------------------------------------------------------------------------+
| `batchCancelPairLimitOrdersWithSigner`_ | Same functionality to ``cancelPairLimitOrders`` but called by a |
| | registered order signer instead of the maker itself. |
+-----------------------------------------+--------------------------------------------------------------------------+
| `getLimitOrderInfo`_ | Returns the state of a given order. |
+-----------------------------------------+--------------------------------------------------------------------------+
| `getLimitOrderHash`_ | Returns the EIP-712 hash for an order. |
+-----------------------------------------+--------------------------------------------------------------------------+
| **RFQ Orders** | **Overview** |
+-----------------------------------------+--------------------------------------------------------------------------+
| `fillRfqOrder`_ | These are analogous to the above LimitOrder functions. |
+-----------------------------------------+ |
| `fillOrKillRfqOrder`_ | |
+-----------------------------------------+ |
| `cancelRfqOrder`_ | |
+-----------------------------------------+ |
| `batchCancelRfqOrders`_ | |
+-----------------------------------------+ |
| `cancelPairRfqOrders`_ | |
+-----------------------------------------+ |
| `cancelPairRfqOrdersWithSigner`_ | |
+-----------------------------------------+ |
| `batchCancelPairRfqOrders`_ | |
+-----------------------------------------+ |
| `batchCancelPairRfqOrdersWithSigner`_ | |
+-----------------------------------------+ |
| `getRfqOrderInfo`_ | |
+-----------------------------------------+ |
| `getRfqOrderHash`_ | |
+-----------------------------------------+--------------------------------------------------------------------------+
| `registerAllowedRfqOrigins`_ | Register tx.origin addresses that are allowed to fill an RFQ order. |
+-----------------------------------------+--------------------------------------------------------------------------+
| `registerAllowedOrderSigner`_ | Register addresses that can sign orders on behalf of ``msg.sender``. |
+-----------------------------------------+--------------------------------------------------------------------------+
| `isValidOrderSigner`_ | Returns whether a given address is allowed to sign orders for a given |
| | maker address. |
+-----------------------------------------+--------------------------------------------------------------------------+
| **Protocol Fees** | **Overview** |
+-----------------------------------------+--------------------------------------------------------------------------+
| `getProtocolFeeMultiplier`_ | Takers of limit orders pay a protocol fee of `Multiplier * tx.gasprice`. |
| | This returns the `Multiplier`. |
+-----------------------------------------+--------------------------------------------------------------------------+
| `transferProtocolFeesForPools`_ | Transfers protocol fees from escrow to the 0x Staking System. |
| | This should be called near the end of each epoch. |
+-----------------------------------------+--------------------------------------------------------------------------+
Limit Orders
@@ -131,7 +146,7 @@ This function cancels a single limit order created by the caller:
)
external;
This function emits an `OrderCancelled <../basics/events.html#ordercancelled>`_ event if the cancellation is successful. The call will revert if ``msg.sender != order.maker``.
This function emits an `OrderCancelled <../basics/events.html#ordercancelled>`_ event if the cancellation is successful. The call will revert if ``msg.sender != order.maker`` or ``!isValidOrderSigner(maker, msg.sender)``.
batchCancelLimitOrders
----------------------
@@ -146,7 +161,7 @@ This function cancels multiple limit orders created by the caller:
)
external;
This function emits an `OrderCancelled <../basics/events.html#ordercancelled>`_ event for each order it cancels. The call will revert if ``msg.sender != order.maker`` for any of the orders.
This function emits an `OrderCancelled <../basics/events.html#ordercancelled>`_ event for each order it cancels. The call will revert if ``msg.sender != order.maker`` or ``!isValidOrderSigner(maker, msg.sender)`` for any of the orders.
cancelPairLimitOrders
---------------------
@@ -162,10 +177,32 @@ This function cancels all limit orders created by the caller with with a maker a
)
external;
This function emits a `PairCancelledLimitOrders <../basics/events.html#paircancelledlimitorders>`_ event, or reverts in one of the following scenarios:
This function emits a `PairCancelledLimitOrders <../basics/events.html#paircancelledlimitorders>`_ event, or reverts if the ``salt`` parameter is ≤ to a previous ``salt``.
- ``msg.sender != order.maker``
- The ``salt`` parameter is ≤ to a previous ``salt``.
cancelPairLimitOrdersWithSigner
-------------------------------
Same functionality as ``cancelPairLimitOrders`` but ``msg.sender`` is a registered order signer instead of the maker itself.
.. code-block:: solidity
/// @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.
/// Subsequent calls to this function with the same maker and pair require the
/// new salt to be >= the old salt.
/// @param maker The maker for which to cancel.
/// @param makerToken The maker token.
/// @param takerToken The taker token.
/// @param minValidSalt The new minimum valid salt.
function cancelPairLimitOrdersWithSigner(
address maker,
IERC20TokenV06 makerToken,
IERC20TokenV06 takerToken,
uint256 minValidSalt
)
external;
Reverts if ``!isValidOrderSigner(maker, msg.sender)``.
batchCancelPairLimitOrders
--------------------------
@@ -183,6 +220,31 @@ This function performs multiple ``cancelPairLimitOrders()`` at once. Each respec
This function emits a `PairCancelledLimitOrders <../basics/events.html#paircancelledlimitorders>`_ event for each market pair it cancels. It reverts if any of the individual cancellations revert.
batchCancelPairLimitOrdersWithSigner
------------------------------------
Same functionality as ``batchCancelPairLimitOrders`` but ``msg.sender`` is a registered order signer instead of the maker itself.
.. code-block:: solidity
/// @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.
/// Subsequent calls to this function with the same maker and pair require the
/// new salt to be >= the old salt.
/// @param maker The maker for which to cancel.
/// @param makerTokens The maker tokens.
/// @param takerTokens The taker tokens.
/// @param minValidSalts The new minimum valid salts.
function batchCancelPairLimitOrdersWithSigner(
address maker,
IERC20TokenV06[] memory makerTokens,
IERC20TokenV06[] memory takerTokens,
uint256[] memory minValidSalts
)
external;
Reverts if ``!isValidOrderSigner(maker, msg.sender)``.
getLimitOrderInfo
-----------------
@@ -369,7 +431,7 @@ Similar to limit orders, RFQ orders can be cancelled on-chain through a variety
)
external;
This function emits an `OrderCancelled <../basics/events.html#ordercancelled>`_ event if the cancellation is successful. The call will revert if ``msg.sender != order.maker``.
This function emits an `OrderCancelled <../basics/events.html#ordercancelled>`_ event if the cancellation is successful. The call will revert if ``msg.sender != order.maker`` or ``!isValidOrderSigner(maker, msg.sender)``.
batchCancelRfqOrders
--------------------
@@ -384,7 +446,7 @@ This function cancels multiple RFQ orders created by the caller:
)
external;
This function emits an `OrderCancelled <../basics/events.html#ordercancelled>`_ event for each order it cancels. The call will revert if ``msg.sender != order.maker`` for any of the orders.
This function emits an `OrderCancelled <../basics/events.html#ordercancelled>`_ event for each order it cancels. The call will revert if ``msg.sender != order.maker`` or ``!isValidOrderSigner(maker, msg.sender)`` for any orders for any of the orders.
cancelPairRfqOrders
-------------------
@@ -400,10 +462,32 @@ This function cancels all RFQ orders created by the caller with with a maker and
)
external;
This function emits a `PairCancelledRfqOrders <../basics/events.html#paircancelledrfqorders>`_ event, or reverts in one of the following scenarios:
This function emits a `PairCancelledRfqOrders <../basics/events.html#paircancelledrfqorders>`_ event, or reverts if the ``salt`` parameter is ≤ to a previous ``salt``.
- ``msg.sender != order.maker``
- The ``salt`` parameter is ≤ to a previous ``salt``.
cancelPairRfqOrdersWithSigner
-----------------------------
Same functionality as ``cancelPairRfqOrders`` but ``msg.sender`` is a registered order signer instead of the maker itself.
.. code-block:: solidity
/// @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.
/// Subsequent calls to this function with the same maker and pair require the
/// new salt to be >= the old salt.
/// @param maker The maker for which to cancel.
/// @param makerToken The maker token.
/// @param takerToken The taker token.
/// @param minValidSalt The new minimum valid salt.
function cancelPairRfqOrdersWithSigner(
address maker,
IERC20TokenV06 makerToken,
IERC20TokenV06 takerToken,
uint256 minValidSalt
)
external;
Reverts if ``!isValidOrderSigner(maker, msg.sender)``.
batchCancelPairRfqOrders
------------------------
@@ -421,6 +505,31 @@ batchCancelPairRfqOrders
This function emits a `PairCancelledRfqOrders <../basics/events.html#paircancelledrfqorders>`_ event for each market pair it cancels. It reverts if any of the individual cancellations revert.
batchCancelPairRfqOrdersWithSigner
----------------------------------
Same functionality as ``batchCancelPairRfqOrders`` but ``msg.sender`` is a registered order signer instead of the maker itself.
.. code-block:: solidity
/// @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.
/// Subsequent calls to this function with the same maker and pair require the
/// new salt to be >= the old salt.
/// @param maker The maker for which to cancel.
/// @param makerTokens The maker tokens.
/// @param takerTokens The taker tokens.
/// @param minValidSalts The new minimum valid salts.
function batchCancelPairRfqOrdersWithSigner(
address maker,
IERC20TokenV06[] memory makerTokens,
IERC20TokenV06[] memory takerTokens,
uint256[] memory minValidSalts
)
external;
Reverts if ``!isValidOrderSigner(maker, msg.sender)``.
getRfqOrderInfo
---------------
@@ -549,6 +658,47 @@ Looking at the 2nd use case, a maker can register valid tx origins using this fu
This function emits a `RfqOrderOriginsAllowed <../basics/events.html#rfqorderoriginsallowed>`_ event.
registerAllowedOrderSigner
--------------------------
Calls to fill functions require a signature provided by the maker. In cases where the signer can't be the maker itself (e.g. a contract wallet), the maker can delegate signing to another address.
To register a new delegated order signer, the maker can call ``registerAllowedOrderSigner`` with ``allowed == true``.
To revoke permission to a signer, the maker can call ``registerAllowedOrderSigner`` with ``allowed == false``.
.. code-block:: solidity
/// @dev Register a signer who can sign on behalf of msg.sender
/// 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;
This function emits an `OrderSignerRegistered <../basics/events.html#ordersignerregistered>`_ event.
isValidOrderSigner
------------------
Returns whether the ``signer`` is allowed to sign orders on behalf of the ``maker``.
.. code-block:: solidity
/// @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);
Protocol Fees
=============

View File

@@ -92,7 +92,7 @@ The ``RFQOrder`` struct has the following fields:
How To Sign
==============
Both Limit & RFQ orders must be signed by the `maker`. This signature is needed to fill an order, see `Basic Functionality <./functions.html>`_.
Both Limit & RFQ orders must be signed by the `maker` or a registered order signer (`registerAllowedOrderSigner <./functions.html#registerallowedrfqorigins>`_). This signature is needed to fill an order, see `Basic Functionality <./functions.html>`_.
The protocol accepts signatures defined by the following struct:

View File

@@ -4,7 +4,7 @@ Protocol Fees
An ETH protocol fee is paid by the Taker each time a `Limit Order <./orders.html#limit-orders>`_ is `filled <./functions.html>`_.
The fee is proportional to the gas cost of filling an order and scales linearly with gas price. The cost is currently ``70k * tx.gasprice``.
Every 10 days, these fees are aggregated and distributed to the makers as a liquidity reward: the reward is proportional to the maker's collected fees and staked ZRX relative to other makers.
At the end of every Staking Epoch, these fees are aggregated and distributed to the makers as a liquidity reward: the reward is proportional to the maker's collected fees and staked ZRX relative to other makers.
To learn more about protocol fees and liquidity incentives, see the `Official Spec <https://github.com/0xProject/0x-protocol-specification/blob/master/staking/staking-specification.md>`_.
.. note::

View File

@@ -1,4 +1,52 @@
[
{
"version": "6.7.0",
"changes": [
{
"note": "Support PancakeSwap V2",
"pr": 211
}
],
"timestamp": 1619481586
},
{
"version": "6.6.1",
"changes": [
{
"note": "Fixing Positive Slippage logic to not force the EP route",
"pr": 209
}
]
},
{
"version": "6.6.0",
"changes": [
{
"note": "Support `Ropsten` network",
"pr": 203
}
],
"timestamp": 1618592834
},
{
"version": "6.5.3",
"changes": [
{
"note": "Apply slippage to bridge orders in consumer",
"pr": 198
}
],
"timestamp": 1618433771
},
{
"timestamp": 1618314654,
"version": "6.5.2",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1618259868,
"version": "6.5.1",

View File

@@ -5,6 +5,26 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v6.7.0 - _April 26, 2021_
* Support PancakeSwap V2 (#211)
## v6.6.1 - _Invalid date_
* Fixing Positive Slippage logic to not force the EP route (#209)
## v6.6.0 - _April 16, 2021_
* Support `Ropsten` network (#203)
## v6.5.3 - _April 14, 2021_
* Apply slippage to bridge orders in consumer (#198)
## v6.5.2 - _April 13, 2021_
* Dependencies updated
## v6.5.1 - _April 12, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/asset-swapper",
"version": "6.5.1",
"version": "6.7.0",
"engines": {
"node": ">=6.12"
},
@@ -62,10 +62,10 @@
"@0x/contract-addresses": "^6.0.0",
"@0x/contract-wrappers": "^13.15.0",
"@0x/contracts-erc20": "^3.3.6",
"@0x/contracts-zero-ex": "^0.21.1",
"@0x/contracts-zero-ex": "^0.22.0",
"@0x/dev-utils": "^4.2.1",
"@0x/json-schemas": "^5.4.1",
"@0x/protocol-utils": "^1.4.1",
"@0x/protocol-utils": "^1.5.0",
"@0x/quote-server": "^5.0.0",
"@0x/types": "^3.3.1",
"@0x/typescript-typings": "^5.1.6",
@@ -96,7 +96,7 @@
"@0x/contracts-test-utils": "^5.3.24",
"@0x/contracts-utils": "^4.7.6",
"@0x/mesh-rpc-client": "^9.4.2",
"@0x/migrations": "^8.0.1",
"@0x/migrations": "^8.0.2",
"@0x/sol-compiler": "^4.6.1",
"@0x/subproviders": "^6.4.1",
"@0x/ts-doc-gen": "^0.0.28",

View File

@@ -23,6 +23,7 @@ import {
CalldataInfo,
ExchangeProxyContractOpts,
MarketBuySwapQuote,
MarketOperation,
MarketSellSwapQuote,
SwapQuote,
SwapQuoteConsumerBase,
@@ -43,6 +44,7 @@ import {
LiquidityProviderFillData,
MooniswapFillData,
OptimizedMarketBridgeOrder,
OptimizedMarketOrder,
UniswapV2FillData,
} from '../utils/market_operation_utils/types';
@@ -136,7 +138,10 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
const buyToken = quote.makerToken;
// Take the bounds from the worst case
const sellAmount = quote.worstCaseQuoteInfo.totalTakerAmount;
const sellAmount = BigNumber.max(
quote.bestCaseQuoteInfo.totalTakerAmount,
quote.worstCaseQuoteInfo.totalTakerAmount,
);
let minBuyAmount = quote.worstCaseQuoteInfo.makerAmount;
let ethAmount = quote.worstCaseQuoteInfo.protocolFeeInWeiAmount;
@@ -144,13 +149,15 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
ethAmount = ethAmount.plus(sellAmount);
}
const slippedOrders = slipNonNativeOrders(quote);
// VIP routes.
if (
this.chainId === ChainId.Mainnet &&
isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.UniswapV2, ERC20BridgeSource.SushiSwap])
) {
const source = quote.orders[0].source;
const fillData = (quote.orders[0] as OptimizedMarketBridgeOrder<UniswapV2FillData>).fillData;
const source = slippedOrders[0].source;
const fillData = (slippedOrders[0] as OptimizedMarketBridgeOrder<UniswapV2FillData>).fillData;
return {
calldataHexString: this._exchangeProxy
.sellToUniswap(
@@ -183,8 +190,8 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
ERC20BridgeSource.SushiSwap,
])
) {
const source = quote.orders[0].source;
const fillData = (quote.orders[0] as OptimizedMarketBridgeOrder<UniswapV2FillData>).fillData;
const source = slippedOrders[0].source;
const fillData = (slippedOrders[0] as OptimizedMarketBridgeOrder<UniswapV2FillData>).fillData;
return {
calldataHexString: this._exchangeProxy
.sellToPancakeSwap(
@@ -213,7 +220,7 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
this.chainId === ChainId.Mainnet &&
isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.LiquidityProvider])
) {
const fillData = (quote.orders[0] as OptimizedMarketBridgeOrder<LiquidityProviderFillData>).fillData;
const fillData = (slippedOrders[0] as OptimizedMarketBridgeOrder<LiquidityProviderFillData>).fillData;
const target = fillData.poolAddress;
return {
calldataHexString: this._exchangeProxy
@@ -238,7 +245,7 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
this.chainId === ChainId.Mainnet &&
isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.Curve, ERC20BridgeSource.Swerve])
) {
const fillData = quote.orders[0].fills[0].fillData as CurveFillData;
const fillData = slippedOrders[0].fills[0].fillData as CurveFillData;
return {
calldataHexString: this._exchangeProxy
.sellToLiquidityProvider(
@@ -267,7 +274,7 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
this.chainId === ChainId.Mainnet &&
isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.Mooniswap])
) {
const fillData = quote.orders[0].fills[0].fillData as MooniswapFillData;
const fillData = slippedOrders[0].fills[0].fillData as MooniswapFillData;
return {
calldataHexString: this._exchangeProxy
.sellToLiquidityProvider(
@@ -289,7 +296,7 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
if (this.chainId === ChainId.Mainnet && isMultiplexBatchFillCompatible(quote, optsWithDefaults)) {
return {
calldataHexString: this._encodeMultiplexBatchFillCalldata(quote),
calldataHexString: this._encodeMultiplexBatchFillCalldata({ ...quote, orders: slippedOrders }),
ethAmount,
toAddress: this._exchangeProxy.address,
allowanceTarget: this._exchangeProxy.address,
@@ -298,7 +305,10 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
}
if (this.chainId === ChainId.Mainnet && isMultiplexMultiHopFillCompatible(quote, optsWithDefaults)) {
return {
calldataHexString: this._encodeMultiplexMultiHopFillCalldata(quote, optsWithDefaults),
calldataHexString: this._encodeMultiplexMultiHopFillCalldata(
{ ...quote, orders: slippedOrders },
optsWithDefaults,
),
ethAmount,
toAddress: this._exchangeProxy.address,
allowanceTarget: this._exchangeProxy.address,
@@ -321,10 +331,10 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
// If it's two hop we have an intermediate token this is needed to encode the individual FQT
// and we also want to ensure no dust amount is left in the flash wallet
const intermediateToken = quote.isTwoHop ? quote.orders[0].makerToken : NULL_ADDRESS;
const intermediateToken = quote.isTwoHop ? slippedOrders[0].makerToken : NULL_ADDRESS;
// This transformer will fill the quote.
if (quote.isTwoHop) {
const [firstHopOrder, secondHopOrder] = quote.orders;
const [firstHopOrder, secondHopOrder] = slippedOrders;
transforms.push({
deploymentNonce: this.transformerNonces.fillQuoteTransformer,
data: encodeFillQuoteTransformerData({
@@ -349,14 +359,13 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
});
} else {
const fillAmount = isBuyQuote(quote) ? quote.makerTokenFillAmount : quote.takerTokenFillAmount;
transforms.push({
deploymentNonce: this.transformerNonces.fillQuoteTransformer,
data: encodeFillQuoteTransformerData({
side: isBuyQuote(quote) ? FillQuoteTransformerSide.Buy : FillQuoteTransformerSide.Sell,
sellToken,
buyToken,
...getFQTTransformerDataFromOptimizedOrders(quote.orders),
...getFQTTransformerDataFromOptimizedOrders(slippedOrders),
refundReceiver: refundReceiver || NULL_ADDRESS,
fillAmount: !isBuyQuote(quote) && shouldSellEntireBalance ? MAX_UINT256 : fillAmount,
}),
@@ -598,3 +607,38 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
.getABIEncodedTransactionData();
}
}
function slipNonNativeOrders(quote: MarketSellSwapQuote | MarketBuySwapQuote): OptimizedMarketOrder[] {
const slippage = getMaxQuoteSlippageRate(quote);
if (!slippage) {
return quote.orders;
}
return quote.orders.map(o => {
if (o.source === ERC20BridgeSource.Native) {
return o;
}
return {
...o,
...(quote.type === MarketOperation.Sell
? { makerAmount: o.makerAmount.times(1 - slippage).integerValue(BigNumber.ROUND_DOWN) }
: { takerAmount: o.takerAmount.times(1 + slippage).integerValue(BigNumber.ROUND_UP) }),
};
});
}
function getMaxQuoteSlippageRate(quote: MarketBuySwapQuote | MarketSellSwapQuote): number {
if (quote.type === MarketOperation.Buy) {
// (worstCaseTaker - bestCaseTaker) / bestCaseTaker
// where worstCaseTaker >= bestCaseTaker
return quote.worstCaseQuoteInfo.takerAmount
.minus(quote.bestCaseQuoteInfo.takerAmount)
.div(quote.bestCaseQuoteInfo.takerAmount)
.toNumber();
}
// (bestCaseMaker - worstCaseMaker) / bestCaseMaker
// where bestCaseMaker >= worstCaseMaker
return quote.bestCaseQuoteInfo.makerAmount
.minus(quote.worstCaseQuoteInfo.makerAmount)
.div(quote.bestCaseQuoteInfo.makerAmount)
.toNumber();
}

View File

@@ -1,6 +1,6 @@
import { FillQuoteTransformerData, FillQuoteTransformerOrderType } from '@0x/protocol-utils';
import { AffiliateFeeType, ExchangeProxyContractOpts, MarketBuySwapQuote, MarketOperation, SwapQuote } from '../types';
import { ExchangeProxyContractOpts, MarketBuySwapQuote, MarketOperation, SwapQuote } from '../types';
import {
createBridgeDataForBridgeOrder,
getErc20BridgeSourceToBridgeSource,
@@ -70,6 +70,7 @@ export function isMultiplexMultiHopFillCompatible(quote: SwapQuote, opts: Exchan
/**
* Returns true iff a quote can be filled via a VIP feature.
*/
export function isDirectSwapCompatible(
quote: SwapQuote,
opts: ExchangeProxyContractOpts,
@@ -163,10 +164,6 @@ export function requiresTransformERC20(opts: ExchangeProxyContractOpts): boolean
if (!opts.affiliateFee.buyTokenFeeAmount.eq(0) || !opts.affiliateFee.sellTokenFeeAmount.eq(0)) {
return true;
}
// Has a positive slippage fee.
if (opts.affiliateFee.feeType === AffiliateFeeType.PositiveSlippageFee) {
return true;
}
// VIP does not support selling the entire balance
if (opts.shouldSellEntireBalance) {
return true;

View File

@@ -591,9 +591,10 @@ function calculateTwoHopQuoteInfo(
: secondHopOrder.makerAmount,
takerAmount: MarketOperation.Sell
? firstHopOrder.takerAmount
: // tslint:disable-next-line: binary-expression-operand-order
firstHopOrder.takerAmount.times(1 + slippage).integerValue(),
totalTakerAmount: firstHopOrder.takerAmount,
: firstHopOrder.takerAmount.times(1 + slippage).integerValue(),
totalTakerAmount: MarketOperation.Sell
? firstHopOrder.takerAmount
: firstHopOrder.takerAmount.times(1 + slippage).integerValue(),
feeTakerTokenAmount: constants.ZERO_AMOUNT,
protocolFeeInWeiAmount: constants.ZERO_AMOUNT,
gas,

View File

@@ -15,6 +15,7 @@ import {
NERVE_BSC_INFOS,
NULL_ADDRESS,
PANCAKESWAP_ROUTER_BY_CHAIN_ID,
PANCAKESWAP_V2_ROUTER_BY_CHAIN_ID,
SADDLE_MAINNET_INFOS,
SHELL_POOLS_BY_CHAIN_ID,
SMOOTHY_BSC_INFOS,
@@ -253,7 +254,8 @@ export function uniswapV2LikeRouterAddress(
| ERC20BridgeSource.CryptoCom
| ERC20BridgeSource.PancakeSwap
| ERC20BridgeSource.BakerySwap
| ERC20BridgeSource.KyberDmm,
| ERC20BridgeSource.KyberDmm
| ERC20BridgeSource.PancakeSwapV2,
): string {
switch (source) {
case ERC20BridgeSource.UniswapV2:
@@ -268,6 +270,8 @@ export function uniswapV2LikeRouterAddress(
return BAKERYSWAP_ROUTER_BY_CHAIN_ID[chainId];
case ERC20BridgeSource.KyberDmm:
return KYBER_DMM_ROUTER_BY_CHAIN_ID[chainId];
case ERC20BridgeSource.PancakeSwapV2:
return PANCAKESWAP_V2_ROUTER_BY_CHAIN_ID[chainId];
default:
throw new Error(`Unknown UniswapV2 like source ${source}`);
}

View File

@@ -1,4 +1,4 @@
import { ChainId } from '@0x/contract-addresses';
import { ChainId, getContractAddressesForChainOrThrow } from '@0x/contract-addresses';
import { FillQuoteTransformerOrderType } from '@0x/protocol-utils';
import { BigNumber } from '@0x/utils';
import { formatBytes32String } from '@ethersproject/strings';
@@ -87,7 +87,13 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.Component,
ERC20BridgeSource.Saddle,
]),
[ChainId.Ropsten]: new SourceFilters([ERC20BridgeSource.Native]),
[ChainId.Ropsten]: new SourceFilters([
ERC20BridgeSource.Kyber,
ERC20BridgeSource.Native,
ERC20BridgeSource.SushiSwap,
ERC20BridgeSource.Uniswap,
ERC20BridgeSource.UniswapV2,
]),
[ChainId.Rinkeby]: new SourceFilters([ERC20BridgeSource.Native]),
[ChainId.Kovan]: new SourceFilters([ERC20BridgeSource.Native]),
[ChainId.Ganache]: new SourceFilters([ERC20BridgeSource.Native]),
@@ -103,6 +109,7 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.PancakeSwap,
ERC20BridgeSource.SushiSwap,
ERC20BridgeSource.Smoothy,
ERC20BridgeSource.PancakeSwapV2,
]),
},
@@ -142,7 +149,13 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.Component,
ERC20BridgeSource.Saddle,
]),
[ChainId.Ropsten]: new SourceFilters([ERC20BridgeSource.Native]),
[ChainId.Ropsten]: new SourceFilters([
ERC20BridgeSource.Kyber,
ERC20BridgeSource.Native,
ERC20BridgeSource.SushiSwap,
ERC20BridgeSource.Uniswap,
ERC20BridgeSource.UniswapV2,
]),
[ChainId.Rinkeby]: new SourceFilters([ERC20BridgeSource.Native]),
[ChainId.Kovan]: new SourceFilters([ERC20BridgeSource.Native]),
[ChainId.Ganache]: new SourceFilters([ERC20BridgeSource.Native]),
@@ -158,6 +171,7 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.PancakeSwap,
ERC20BridgeSource.SushiSwap,
ERC20BridgeSource.Smoothy,
ERC20BridgeSource.PancakeSwapV2,
]),
},
new SourceFilters([]),
@@ -175,6 +189,7 @@ export const FEE_QUOTE_SOURCES_BY_CHAIN_ID = valueByChainId<ERC20BridgeSource[]>
{
[ChainId.Mainnet]: [ERC20BridgeSource.UniswapV2, ERC20BridgeSource.SushiSwap],
[ChainId.BSC]: [ERC20BridgeSource.PancakeSwap, ERC20BridgeSource.Mooniswap, ERC20BridgeSource.SushiSwap],
[ChainId.Ropsten]: [ERC20BridgeSource.UniswapV2, ERC20BridgeSource.SushiSwap],
},
[],
);
@@ -369,6 +384,7 @@ export const DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID = valueByChainId<string[]>(
'0x2170ed0880ac9a755fd29b2688956bd959f933f8', // ETH
'0x55d398326f99059ff775485246999027b3197955', // BUSD-T
],
[ChainId.Ropsten]: [getContractAddressesForChainOrThrow(ChainId.Ropsten).etherToken],
},
[],
);
@@ -397,8 +413,12 @@ export const DEFAULT_TOKEN_ADJACENCY_GRAPH_BY_CHAIN_ID = valueByChainId<TokenAdj
export const NATIVE_FEE_TOKEN_BY_CHAIN_ID = valueByChainId<string>(
{
[ChainId.Mainnet]: MAINNET_TOKENS.WETH,
[ChainId.BSC]: '0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c', // WBNB
[ChainId.Mainnet]: getContractAddressesForChainOrThrow(ChainId.Mainnet).etherToken,
[ChainId.BSC]: getContractAddressesForChainOrThrow(ChainId.BSC).etherToken,
[ChainId.Ganache]: getContractAddressesForChainOrThrow(ChainId.Ganache).etherToken,
[ChainId.Ropsten]: getContractAddressesForChainOrThrow(ChainId.Ropsten).etherToken,
[ChainId.Rinkeby]: getContractAddressesForChainOrThrow(ChainId.Rinkeby).etherToken,
[ChainId.Kovan]: getContractAddressesForChainOrThrow(ChainId.Kovan).etherToken,
},
NULL_ADDRESS,
);
@@ -732,6 +752,11 @@ export const KYBER_CONFIG_BY_CHAIN_ID = valueByChainId<KyberSamplerOpts>(
hintHandler: '0xa1C0Fa73c39CFBcC11ec9Eb1Afc665aba9996E2C',
weth: MAINNET_TOKENS.WETH,
},
[ChainId.Ropsten]: {
networkProxy: '0x818e6fecd516ecc3849daf6845e3ec868087b755',
hintHandler: '0x63f773c026093eef988e803bdd5772dd235a8e71',
weth: getContractAddressesForChainOrThrow(ChainId.Ropsten).etherToken,
},
},
{
networkProxy: NULL_ADDRESS,
@@ -768,6 +793,7 @@ export const LIQUIDITY_PROVIDER_REGISTRY_BY_CHAIN_ID = valueByChainId<LiquidityP
export const UNISWAPV1_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
{
[ChainId.Mainnet]: '0xc0a47dfe034b400b47bdad5fecda2621de6c4d95',
[ChainId.Ropsten]: '0x9c83dce8ca20e9aaf9d3efc003b2ea62abc08351',
},
NULL_ADDRESS,
);
@@ -775,6 +801,7 @@ export const UNISWAPV1_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
export const UNISWAPV2_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
{
[ChainId.Mainnet]: '0xf164fc0ec4e93095b804a4795bbe1e041497b92a',
[ChainId.Ropsten]: '0xf164fc0ec4e93095b804a4795bbe1e041497b92a',
},
NULL_ADDRESS,
);
@@ -783,6 +810,7 @@ export const SUSHISWAP_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
{
[ChainId.Mainnet]: '0xd9e1ce17f2641f24ae83637ab66a2cca9c378b9f',
[ChainId.BSC]: '0x1b02da8cb0d097eb8d57a175b88c7d8b47997506',
[ChainId.Ropsten]: '0x1b02da8cb0d097eb8d57a175b88c7d8b47997506',
},
NULL_ADDRESS,
);
@@ -964,6 +992,13 @@ export const PANCAKESWAP_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
NULL_ADDRESS,
);
export const PANCAKESWAP_V2_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
{
[ChainId.BSC]: '0x10ed43c718714eb63d5aa57b78b54704e256024e',
},
NULL_ADDRESS,
);
export const BAKERYSWAP_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
{
[ChainId.BSC]: '0xcde540d7eafe93ac5fe6233bee57e1270d3e330f',
@@ -1110,6 +1145,15 @@ export const DEFAULT_GAS_SCHEDULE: Required<FeeSchedule> = {
}
return gas;
},
[ERC20BridgeSource.PancakeSwapV2]: (fillData?: FillData) => {
// TODO: Different base cost if to/from ETH.
let gas = 90e3;
const path = (fillData as UniswapV2FillData).tokenAddressPath;
if (path.length > 2) {
gas += (path.length - 2) * 60e3; // +60k for each hop.
}
return gas;
},
};
export const DEFAULT_FEE_SCHEDULE: Required<FeeSchedule> = { ...DEFAULT_GAS_SCHEDULE };

View File

@@ -134,6 +134,8 @@ export function getErc20BridgeSourceToBridgeSource(source: ERC20BridgeSource): s
return encodeBridgeSourceId(BridgeProtocol.Curve, 'Smoothy');
case ERC20BridgeSource.Saddle:
return encodeBridgeSourceId(BridgeProtocol.Nerve, 'Saddle');
case ERC20BridgeSource.PancakeSwapV2:
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'PancakeSwapV2');
default:
throw new Error(AggregationError.NoBridgeForSource);
}
@@ -187,6 +189,7 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
case ERC20BridgeSource.PancakeSwap:
case ERC20BridgeSource.BakerySwap:
case ERC20BridgeSource.KyberDmm:
case ERC20BridgeSource.PancakeSwapV2:
const uniswapV2FillData = (order as OptimizedMarketBridgeOrder<UniswapV2FillData>).fillData;
bridgeData = encoder.encode([uniswapV2FillData.router, uniswapV2FillData.tokenAddressPath]);
break;
@@ -334,6 +337,7 @@ export const BRIDGE_ENCODERS: {
// BSC
[ERC20BridgeSource.PancakeSwap]: routerAddressPathEncoder,
[ERC20BridgeSource.BakerySwap]: routerAddressPathEncoder,
[ERC20BridgeSource.PancakeSwapV2]: routerAddressPathEncoder,
};
function getFillTokenAmounts(fill: CollapsedFill, side: MarketOperation): [BigNumber, BigNumber] {

View File

@@ -1088,6 +1088,7 @@ export class SamplerOperations {
case ERC20BridgeSource.PancakeSwap:
case ERC20BridgeSource.BakerySwap:
case ERC20BridgeSource.KyberDmm:
case ERC20BridgeSource.PancakeSwapV2:
const uniLikeRouter = uniswapV2LikeRouterAddress(this.chainId, source);
if (!isValidAddress(uniLikeRouter)) {
return [];
@@ -1297,6 +1298,7 @@ export class SamplerOperations {
case ERC20BridgeSource.PancakeSwap:
case ERC20BridgeSource.BakerySwap:
case ERC20BridgeSource.KyberDmm:
case ERC20BridgeSource.PancakeSwapV2:
const uniLikeRouter = uniswapV2LikeRouterAddress(this.chainId, source);
if (!isValidAddress(uniLikeRouter)) {
return [];

View File

@@ -69,6 +69,7 @@ export enum ERC20BridgeSource {
Nerve = 'Nerve',
Belt = 'Belt',
Ellipsis = 'Ellipsis',
PancakeSwapV2 = 'PancakeSwap_V2',
}
// tslint:disable: enum-naming

View File

@@ -83,7 +83,7 @@ export class QuoteRequestor {
sellTokenAddress: string, // taker token
assetFillAmount: BigNumber,
comparisonPrice?: BigNumber,
isLastLook: boolean = false,
isLastLook?: boolean | undefined,
): TakerRequestQueryParams {
const { buyAmountBaseUnits, sellAmountBaseUnits } =
marketOperation === MarketOperation.Buy
@@ -111,9 +111,11 @@ export class QuoteRequestor {
buyTokenAddress,
sellTokenAddress,
comparisonPrice: comparisonPrice === undefined ? undefined : comparisonPrice.toString(),
isLastLook: isLastLook.toString(),
protocolVersion: '4',
};
if (isLastLook) {
requestParamsWithBigNumbers.isLastLook = isLastLook.toString();
}
// convert BigNumbers to strings
// so they are digestible by axios

View File

@@ -129,11 +129,10 @@ export function simulateWorstCaseFill(quoteInfo: QuoteFillInfo): QuoteFillResult
};
// Adjust the output by 1-slippage for the worst case if it is a sell
// Adjust the output by 1+slippage for the worst case if it is a buy
const outputMultiplier =
result.output =
quoteInfo.side === MarketOperation.Sell
? new BigNumber(1).minus(opts.slippage)
: new BigNumber(1).plus(opts.slippage);
result.output = result.output.times(outputMultiplier).integerValue();
? result.output.times(1 - opts.slippage).integerValue(BigNumber.ROUND_DOWN)
: result.output.times(1 + opts.slippage).integerValue(BigNumber.ROUND_UP);
return fromIntermediateQuoteFillResult(result, quoteInfo);
}

View File

@@ -72,6 +72,7 @@ const DEFAULT_EXCLUDED = [
ERC20BridgeSource.Smoothy,
ERC20BridgeSource.Component,
ERC20BridgeSource.Saddle,
ERC20BridgeSource.PancakeSwapV2,
];
const BUY_SOURCES = BUY_SOURCE_FILTER_BY_CHAIN_ID[ChainId.Mainnet].sources;
const SELL_SOURCES = SELL_SOURCE_FILTER_BY_CHAIN_ID[ChainId.Mainnet].sources;
@@ -915,6 +916,7 @@ describe('MarketOperationUtils tests', () => {
intentOnFilling: true,
quoteRequestor: {
requestRfqtIndicativeQuotesAsync: requestor.object.requestRfqtIndicativeQuotesAsync,
getMakerUriForSignature: requestor.object.getMakerUriForSignature,
} as any,
},
},

View File

@@ -272,7 +272,6 @@ describe('QuoteRequestor', async () => {
comparisonPrice: undefined,
takerAddress,
txOrigin,
isLastLook: 'false',
protocolVersion: '4',
};
const mockedDefaults = {
@@ -579,7 +578,6 @@ describe('QuoteRequestor', async () => {
comparisonPrice: undefined,
takerAddress,
txOrigin: takerAddress,
isLastLook: 'false',
protocolVersion: '4',
};
const mockedDefaults = {
@@ -688,7 +686,6 @@ describe('QuoteRequestor', async () => {
takerAddress,
txOrigin: takerAddress,
protocolVersion: '4',
isLastLook: 'false',
};
const mockedDefaults = {
requestApiKey: apiKey,
@@ -773,7 +770,6 @@ describe('QuoteRequestor', async () => {
comparisonPrice: undefined,
takerAddress,
txOrigin: takerAddress,
isLastLook: 'false',
protocolVersion: '4',
};
// Successful response

View File

@@ -1,7 +1,8 @@
{
"extends": ["@0x/tslint-config"],
"rules": {
"max-file-line-count": false
"max-file-line-count": false,
"binary-expression-operand-order": false
},
"linterOptions": {
"exclude": ["src/artifacts.ts", "test/artifacts.ts"]

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contract-wrappers-test",
"version": "12.2.40",
"version": "12.2.41",
"engines": {
"node": ">=6.12"
},
@@ -34,7 +34,7 @@
"@0x/contract-wrappers": "^13.15.0",
"@0x/contracts-test-utils": "^5.3.24",
"@0x/dev-utils": "^4.2.1",
"@0x/migrations": "^8.0.1",
"@0x/migrations": "^8.0.2",
"@0x/order-utils": "^10.4.19",
"@0x/subproviders": "^6.4.1",
"@0x/ts-doc-gen": "^0.0.28",

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1619481586,
"version": "8.0.2",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1618259868,
"version": "8.0.1",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v8.0.2 - _April 26, 2021_
* Dependencies updated
## v8.0.1 - _April 12, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/migrations",
"version": "8.0.1",
"version": "8.0.2",
"engines": {
"node": ">=6.12"
},
@@ -81,7 +81,7 @@
"@0x/contracts-multisig": "^4.1.28",
"@0x/contracts-staking": "^2.0.35",
"@0x/contracts-utils": "^4.7.6",
"@0x/contracts-zero-ex": "^0.21.1",
"@0x/contracts-zero-ex": "^0.22.0",
"@0x/sol-compiler": "^4.6.1",
"@0x/subproviders": "^6.4.1",
"@0x/typescript-typings": "^5.1.6",

View File

@@ -1,4 +1,14 @@
[
{
"version": "1.5.0",
"changes": [
{
"note": "Add support for orderSignerRegistry in getSignatureWithProviderAsync()",
"pr": 195
}
],
"timestamp": 1619481586
},
{
"timestamp": 1618259868,
"version": "1.4.1",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v1.5.0 - _April 26, 2021_
* Add support for orderSignerRegistry in getSignatureWithProviderAsync() (#195)
## v1.4.1 - _April 12, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/protocol-utils",
"version": "1.4.1",
"version": "1.5.0",
"engines": {
"node": ">=6.12"
},

View File

@@ -114,12 +114,13 @@ export abstract class OrderBase {
public async getSignatureWithProviderAsync(
provider: SupportedProvider,
type: SignatureType = SignatureType.EthSign,
signer: string = this.maker,
): Promise<Signature> {
switch (type) {
case SignatureType.EIP712:
return eip712SignTypedDataWithProviderAsync(this.getEIP712TypedData(), this.maker, provider);
return eip712SignTypedDataWithProviderAsync(this.getEIP712TypedData(), signer, provider);
case SignatureType.EthSign:
return ethSignHashWithProviderAsync(this.getHash(), this.maker, provider);
return ethSignHashWithProviderAsync(this.getHash(), signer, provider);
default:
throw new Error(`Cannot sign with signature type: ${type}`);
}

View File

@@ -49,6 +49,15 @@ export class OrderNotSignedByMakerError extends RevertError {
}
}
export class InvalidSignerError extends RevertError {
constructor(maker?: string, signer?: string) {
super('InvalidSignerError', 'InvalidSignerError(address maker, address signer)', {
maker,
signer,
});
}
}
export class OrderNotFillableBySenderError extends RevertError {
constructor(orderHash?: string, sender?: string, orderSender?: string) {
super(
@@ -135,6 +144,7 @@ const types = [
FillOrKillFailedError,
OnlyOrderMakerAllowed,
BatchFillIncompleteError,
InvalidSignerError,
];
// Register the types we've defined.