-
-
Save alexkroeger/f29fd11120b0fd50f5ab780732e1c5e8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/contracts/zero-ex/contracts/src/errors/LibNativeOrdersRichErrors.sol b/contracts/zero-ex/contracts/src/errors/LibNativeOrdersRichErrors.sol | |
index e339caac4..a12ab1428 100644 | |
--- a/contracts/zero-ex/contracts/src/errors/LibNativeOrdersRichErrors.sol | |
+++ b/contracts/zero-ex/contracts/src/errors/LibNativeOrdersRichErrors.sol | |
@@ -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, | |
diff --git a/contracts/zero-ex/contracts/src/features/NativeOrdersFeature.sol b/contracts/zero-ex/contracts/src/features/NativeOrdersFeature.sol | |
index 93684b4da..100e3ebe2 100644 | |
--- a/contracts/zero-ex/contracts/src/features/NativeOrdersFeature.sol | |
+++ b/contracts/zero-ex/contracts/src/features/NativeOrdersFeature.sol | |
@@ -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; | |
} | |
} | |
diff --git a/contracts/zero-ex/contracts/src/features/interfaces/INativeOrdersEvents.sol b/contracts/zero-ex/contracts/src/features/interfaces/INativeOrdersEvents.sol | |
index d055f1705..ff7c7810b 100644 | |
--- a/contracts/zero-ex/contracts/src/features/interfaces/INativeOrdersEvents.sol | |
+++ b/contracts/zero-ex/contracts/src/features/interfaces/INativeOrdersEvents.sol | |
@@ -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 | |
+ ); | |
} | |
diff --git a/contracts/zero-ex/contracts/src/features/interfaces/INativeOrdersFeature.sol b/contracts/zero-ex/contracts/src/features/interfaces/INativeOrdersFeature.sol | |
index 9ddb5306b..b482674cf 100644 | |
--- a/contracts/zero-ex/contracts/src/features/interfaces/INativeOrdersFeature.sol | |
+++ b/contracts/zero-ex/contracts/src/features/interfaces/INativeOrdersFeature.sol | |
@@ -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); | |
} | |
diff --git a/contracts/zero-ex/contracts/src/features/native_orders/NativeOrdersCancellation.sol b/contracts/zero-ex/contracts/src/features/native_orders/NativeOrdersCancellation.sol | |
index b90f0a9c9..00a34e4be 100644 | |
--- a/contracts/zero-ex/contracts/src/features/native_orders/NativeOrdersCancellation.sol | |
+++ b/contracts/zero-ex/contracts/src/features/native_orders/NativeOrdersCancellation.sol | |
@@ -55,7 +55,7 @@ abstract contract NativeOrdersCancellation is | |
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, | |
@@ -72,7 +72,7 @@ abstract contract NativeOrdersCancellation is | |
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, | |
@@ -118,33 +118,34 @@ abstract contract NativeOrdersCancellation is | |
) | |
public | |
{ | |
- LibNativeOrdersStorage.Storage storage stor = | |
- LibNativeOrdersStorage.getStorage(); | |
- | |
- uint256 oldMinValidSalt = | |
- stor.limitOrdersMakerToMakerTokenToTakerTokenToMinValidOrderSalt | |
- [msg.sender] | |
- [address(makerToken)] | |
- [address(takerToken)]; | |
+ _cancelPairLimitOrders(msg.sender, makerToken, takerToken, minValidSalt); | |
+ } | |
- // 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(); | |
- | |
- uint256 oldMinValidSalt = | |
- stor.rfqOrdersMakerToMakerTokenToTakerTokenToMinValidOrderSalt | |
- [msg.sender] | |
- [address(makerToken)] | |
- [address(takerToken)]; | |
+ _cancelPairRfqOrders(msg.sender, makerToken, takerToken, minValidSalt); | |
+ } | |
- // 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 | |
+ ); | |
+ } | |
} | |
diff --git a/contracts/zero-ex/contracts/src/features/native_orders/NativeOrdersInfo.sol b/contracts/zero-ex/contracts/src/features/native_orders/NativeOrdersInfo.sol | |
index 47d6fde3b..16ea926c8 100644 | |
--- a/contracts/zero-ex/contracts/src/features/native_orders/NativeOrdersInfo.sol | |
+++ b/contracts/zero-ex/contracts/src/features/native_orders/NativeOrdersInfo.sol | |
@@ -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]; | |
+ } | |
} | |
diff --git a/contracts/zero-ex/contracts/src/features/native_orders/NativeOrdersSettlement.sol b/contracts/zero-ex/contracts/src/features/native_orders/NativeOrdersSettlement.sol | |
index 7c030e184..ea7e832a4 100644 | |
--- a/contracts/zero-ex/contracts/src/features/native_orders/NativeOrdersSettlement.sol | |
+++ b/contracts/zero-ex/contracts/src/features/native_orders/NativeOrdersSettlement.sol | |
@@ -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); | |
+ } | |
} | |
diff --git a/contracts/zero-ex/contracts/src/storage/LibNativeOrdersStorage.sol b/contracts/zero-ex/contracts/src/storage/LibNativeOrdersStorage.sol | |
index a4adf413c..ceb899281 100644 | |
--- a/contracts/zero-ex/contracts/src/storage/LibNativeOrdersStorage.sol | |
+++ b/contracts/zero-ex/contracts/src/storage/LibNativeOrdersStorage.sol | |
@@ -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. | |
diff --git a/contracts/zero-ex/contracts/src/transformers/bridges/BridgeAdapter.sol b/contracts/zero-ex/contracts/src/transformers/bridges/BridgeAdapter.sol | |
index 8db42204d..590d4f1fa 100644 | |
--- a/contracts/zero-ex/contracts/src/transformers/bridges/BridgeAdapter.sol | |
+++ b/contracts/zero-ex/contracts/src/transformers/bridges/BridgeAdapter.sol | |
@@ -30,6 +30,7 @@ import "./mixins/MixinCryptoCom.sol"; | |
import "./mixins/MixinDodo.sol"; | |
import "./mixins/MixinDodoV2.sol"; | |
import "./mixins/MixinKyber.sol"; | |
+import "./mixins/MixinMakerPSM.sol"; | |
import "./mixins/MixinMooniswap.sol"; | |
import "./mixins/MixinMStable.sol"; | |
import "./mixins/MixinNerve.sol"; | |
@@ -49,6 +50,7 @@ contract BridgeAdapter is | |
MixinDodo, | |
MixinDodoV2, | |
MixinKyber, | |
+ MixinMakerPSM, | |
MixinMooniswap, | |
MixinMStable, | |
MixinNerve, | |
@@ -68,6 +70,7 @@ contract BridgeAdapter is | |
MixinDodo() | |
MixinDodoV2() | |
MixinKyber(weth) | |
+ MixinMakerPSM() | |
MixinMooniswap(weth) | |
MixinMStable() | |
MixinNerve() | |
@@ -123,6 +126,13 @@ contract BridgeAdapter is | |
sellAmount, | |
order.bridgeData | |
); | |
+ } else if (protocolId == BridgeProtocols.MAKERPSM) { | |
+ boughtAmount = _tradeMakerPsm( | |
+ sellToken, | |
+ buyToken, | |
+ sellAmount, | |
+ order.bridgeData | |
+ ); | |
} else if (protocolId == BridgeProtocols.MOONISWAP) { | |
boughtAmount = _tradeMooniswap( | |
sellToken, | |
diff --git a/contracts/zero-ex/contracts/src/transformers/bridges/BridgeProtocols.sol b/contracts/zero-ex/contracts/src/transformers/bridges/BridgeProtocols.sol | |
index baebf8ac0..60630fa7c 100644 | |
--- a/contracts/zero-ex/contracts/src/transformers/bridges/BridgeProtocols.sol | |
+++ b/contracts/zero-ex/contracts/src/transformers/bridges/BridgeProtocols.sol | |
@@ -43,4 +43,5 @@ library BridgeProtocols { | |
uint128 internal constant BANCOR = 13; | |
uint128 internal constant COFIX = 14; | |
uint128 internal constant NERVE = 15; | |
+ uint128 internal constant MAKERPSM = 16; | |
} | |
diff --git a/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinMakerPSM.sol b/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinMakerPSM.sol | |
new file mode 100644 | |
index 000000000..283e9873f | |
--- /dev/null | |
+++ b/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinMakerPSM.sol | |
@@ -0,0 +1,114 @@ | |
+// 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-erc20/contracts/src/v06/LibERC20TokenV06.sol"; | |
+import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol"; | |
+import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol"; | |
+ | |
+interface IPSM { | |
+ // @dev Get the fee for selling USDC to DAI in PSM | |
+ // @return tin toll in [wad] | |
+ function tin() external view returns (uint256); | |
+ // @dev Get the fee for selling DAI to USDC in PSM | |
+ // @return tout toll out [wad] | |
+ function tout() external view returns (uint256); | |
+ | |
+ // @dev Get the address of the PSM state Vat | |
+ // @return address of the Vat | |
+ function vat() external view returns (address); | |
+ | |
+ // @dev Get the address of the underlying vault powering PSM | |
+ // @return address of gemJoin contract | |
+ function gemJoin() external view returns (address); | |
+ | |
+ // @dev Sell USDC for DAI | |
+ // @param usr The address of the account trading USDC for DAI. | |
+ // @param gemAmt The amount of USDC to sell in USDC base units | |
+ function sellGem( | |
+ address usr, | |
+ uint256 gemAmt | |
+ ) external; | |
+ // @dev Buy USDC for DAI | |
+ // @param usr The address of the account trading DAI for USDC | |
+ // @param gemAmt The amount of USDC to buy in USDC base units | |
+ function buyGem( | |
+ address usr, | |
+ uint256 gemAmt | |
+ ) external; | |
+} | |
+ | |
+contract MixinMakerPSM { | |
+ | |
+ using LibERC20TokenV06 for IERC20TokenV06; | |
+ using LibSafeMathV06 for uint256; | |
+ | |
+ struct MakerPsmBridgeData { | |
+ address psmAddress; | |
+ address gemTokenAddres; | |
+ } | |
+ | |
+ // Maker units | |
+ // wad: fixed point decimal with 18 decimals (for basic quantities, e.g. balances) | |
+ uint256 constant private WAD = 10 ** 18; | |
+ // ray: fixed point decimal with 27 decimals (for precise quantites, e.g. ratios) | |
+ uint256 constant private RAY = 10 ** 27; | |
+ // rad: fixed point decimal with 45 decimals (result of integer multiplication with a wad and a ray) | |
+ uint256 constant private RAD = 10 ** 45; | |
+ // See https://github.com/makerdao/dss/blob/master/DEVELOPING.md | |
+ | |
+ function _tradeMakerPsm( | |
+ IERC20TokenV06 sellToken, | |
+ IERC20TokenV06 buyToken, | |
+ uint256 sellAmount, | |
+ bytes memory bridgeData | |
+ ) | |
+ internal | |
+ returns (uint256 boughtAmount) | |
+ { | |
+ // Decode the bridge data. | |
+ MakerPsmBridgeData memory data = abi.decode(bridgeData, (MakerPsmBridgeData)); | |
+ uint256 beforeBalance = buyToken.balanceOf(address(this)); | |
+ | |
+ IPSM psm = IPSM(data.psmAddress); | |
+ | |
+ if (address(sellToken) == data.gemTokenAddres) { | |
+ sellToken.approveIfBelow( | |
+ psm.gemJoin(), | |
+ sellAmount | |
+ ); | |
+ | |
+ psm.sellGem(address(this), sellAmount); | |
+ } else if (address(buyToken) == data.gemTokenAddres) { | |
+ uint256 feeDivisor = WAD.safeAdd(psm.tout()); // eg. 1.001 * 10 ** 18 with 0.1% fee [tout is in wad]; | |
+ uint256 buyTokenBaseUnit = uint256(10) ** uint256(buyToken.decimals()); | |
+ uint256 gemAmount = sellAmount.safeMul(buyTokenBaseUnit).safeDiv(feeDivisor); | |
+ | |
+ sellToken.approveIfBelow( | |
+ data.psmAddress, | |
+ sellAmount | |
+ ); | |
+ psm.buyGem(address(this), gemAmount); | |
+ } | |
+ | |
+ return buyToken.balanceOf(address(this)).safeSub(beforeBalance); | |
+ } | |
+} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment