Skip to content
This repository was archived by the owner on Apr 27, 2022. It is now read-only.

Commit 2598df9

Browse files
authored
Convert signing library to a mixin (#399)
This PR converts the `GPv2Siging` library to a mixin (i.e. abstract contract). This allows a couple things: - The domain separator does not need to be passed around everywhere - It can have storage for `presign` scheme ### Test Plan Adjusted tests still pass.
1 parent 47b2484 commit 2598df9

File tree

8 files changed

+86
-135
lines changed

8 files changed

+86
-135
lines changed

hardhat.config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ export default {
6565
],
6666
},
6767
networks: {
68+
hardhat: {
69+
blockGasLimit: 12.5e6,
70+
},
6871
mainnet: {
6972
...sharedNetworkConfig,
7073
url: `https://mainnet.infura.io/v3/${INFURA_KEY}`,

src/contracts/GPv2Settlement.sol

Lines changed: 5 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -13,35 +13,15 @@ import "./libraries/GPv2Interaction.sol";
1313
import "./libraries/GPv2Order.sol";
1414
import "./libraries/GPv2Trade.sol";
1515
import "./libraries/GPv2TradeExecution.sol";
16+
import "./mixins/GPv2Signing.sol";
1617

1718
/// @title Gnosis Protocol v2 Settlement Contract
1819
/// @author Gnosis Developers
19-
contract GPv2Settlement is ReentrancyGuard, StorageAccessible {
20+
contract GPv2Settlement is GPv2Signing, ReentrancyGuard, StorageAccessible {
2021
using GPv2Order for bytes;
21-
using GPv2Signing for GPv2Signing.RecoveredOrder;
2222
using GPv2TradeExecution for GPv2TradeExecution.Data;
2323
using SafeMath for uint256;
2424

25-
/// @dev The EIP-712 domain type hash used for computing the domain
26-
/// separator.
27-
bytes32 private constant DOMAIN_TYPE_HASH =
28-
keccak256(
29-
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
30-
);
31-
32-
/// @dev The EIP-712 domain name used for computing the domain separator.
33-
bytes32 private constant DOMAIN_NAME = keccak256("Gnosis Protocol");
34-
35-
/// @dev The EIP-712 domain version used for computing the domain separator.
36-
bytes32 private constant DOMAIN_VERSION = keccak256("v2");
37-
38-
/// @dev The domain separator used for signing orders that gets mixed in
39-
/// making signatures for different domains incompatible. This domain
40-
/// separator is computed following the EIP-712 standard and has replay
41-
/// protection mixed in so that signed orders are only valid for specific
42-
/// GPv2 contracts.
43-
bytes32 public immutable domainSeparator;
44-
4525
/// @dev The authenticator is used to determine who can call the settle function.
4626
/// That is, only authorised solvers have the ability to invoke settlements.
4727
/// Any valid authenticator implements an isSolver method called by the onlySolver
@@ -86,24 +66,6 @@ contract GPv2Settlement is ReentrancyGuard, StorageAccessible {
8666

8767
constructor(GPv2Authentication authenticator_) {
8868
authenticator = authenticator_;
89-
90-
// NOTE: Currently, the only way to get the chain ID in solidity is
91-
// using assembly.
92-
uint256 chainId;
93-
// solhint-disable-next-line no-inline-assembly
94-
assembly {
95-
chainId := chainid()
96-
}
97-
98-
domainSeparator = keccak256(
99-
abi.encode(
100-
DOMAIN_TYPE_HASH,
101-
DOMAIN_NAME,
102-
DOMAIN_VERSION,
103-
chainId,
104-
address(this)
105-
)
106-
);
10769
allowanceManager = new GPv2AllowanceManager();
10870
}
10971

@@ -198,18 +160,13 @@ contract GPv2Settlement is ReentrancyGuard, StorageAccessible {
198160
uint256[] calldata clearingPrices,
199161
GPv2Trade.Data[] calldata trades
200162
) internal returns (GPv2TradeExecution.Data[] memory executedTrades) {
201-
GPv2Signing.RecoveredOrder memory recoveredOrder =
202-
GPv2Signing.allocateRecoveredOrder();
163+
RecoveredOrder memory recoveredOrder = allocateRecoveredOrder();
203164

204165
executedTrades = new GPv2TradeExecution.Data[](trades.length);
205166
for (uint256 i = 0; i < trades.length; i++) {
206167
GPv2Trade.Data calldata trade = trades[i];
207168

208-
recoveredOrder.recoverOrderFromTrade(
209-
domainSeparator,
210-
tokens,
211-
trade
212-
);
169+
recoverOrderFromTrade(recoveredOrder, tokens, trade);
213170
computeTradeExecution(
214171
recoveredOrder,
215172
clearingPrices[trade.sellTokenIndex],
@@ -234,7 +191,7 @@ contract GPv2Settlement is ReentrancyGuard, StorageAccessible {
234191
/// @param feeDiscount The discount to apply to the final executed fees.
235192
/// @param executedTrade Memory location for computed executed trade data.
236193
function computeTradeExecution(
237-
GPv2Signing.RecoveredOrder memory recoveredOrder,
194+
RecoveredOrder memory recoveredOrder,
238195
uint256 sellPrice,
239196
uint256 buyPrice,
240197
uint256 executedAmount,

src/contracts/libraries/GPv2Trade.sol

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@
22
pragma solidity ^0.7.6;
33

44
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
5+
import "../mixins/GPv2Signing.sol";
56
import "./GPv2Order.sol";
6-
import "./GPv2Signing.sol";
77

88
/// @title Gnosis Protocol v2 Trade Library.
99
/// @author Gnosis Developers
1010
library GPv2Trade {
1111
using GPv2Order for GPv2Order.Data;
1212
using GPv2Order for bytes;
13-
using GPv2Signing for GPv2Order.Data;
1413

1514
/// @dev A struct representing a trade to be executed as part a batch
1615
/// settlement.

src/contracts/libraries/GPv2Signing.sol renamed to src/contracts/mixins/GPv2Signing.sol

Lines changed: 50 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
pragma solidity ^0.7.6;
33

44
import "../interfaces/GPv2EIP1271.sol";
5-
import "./GPv2Order.sol";
6-
import "./GPv2Trade.sol";
5+
import "../libraries/GPv2Order.sol";
6+
import "../libraries/GPv2Trade.sol";
77

88
/// @title Gnosis Protocol v2 Signing Library.
99
/// @author Gnosis Developers
10-
library GPv2Signing {
10+
abstract contract GPv2Signing {
1111
using GPv2Order for GPv2Order.Data;
1212
using GPv2Order for bytes;
1313

@@ -22,6 +22,46 @@ library GPv2Signing {
2222
/// @dev Signing scheme used for recovery.
2323
enum Scheme {Eip712, EthSign, Eip1271}
2424

25+
/// @dev The EIP-712 domain type hash used for computing the domain
26+
/// separator.
27+
bytes32 private constant DOMAIN_TYPE_HASH =
28+
keccak256(
29+
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
30+
);
31+
32+
/// @dev The EIP-712 domain name used for computing the domain separator.
33+
bytes32 private constant DOMAIN_NAME = keccak256("Gnosis Protocol");
34+
35+
/// @dev The EIP-712 domain version used for computing the domain separator.
36+
bytes32 private constant DOMAIN_VERSION = keccak256("v2");
37+
38+
/// @dev The domain separator used for signing orders that gets mixed in
39+
/// making signatures for different domains incompatible. This domain
40+
/// separator is computed following the EIP-712 standard and has replay
41+
/// protection mixed in so that signed orders are only valid for specific
42+
/// GPv2 contracts.
43+
bytes32 public immutable domainSeparator;
44+
45+
constructor() {
46+
// NOTE: Currently, the only way to get the chain ID in solidity is
47+
// using assembly.
48+
uint256 chainId;
49+
// solhint-disable-next-line no-inline-assembly
50+
assembly {
51+
chainId := chainid()
52+
}
53+
54+
domainSeparator = keccak256(
55+
abi.encode(
56+
DOMAIN_TYPE_HASH,
57+
DOMAIN_NAME,
58+
DOMAIN_VERSION,
59+
chainId,
60+
address(this)
61+
)
62+
);
63+
}
64+
2565
/// @dev Returns an empty recovered order with a pre-allocated buffer for
2666
/// packing the unique identifier.
2767
///
@@ -38,27 +78,19 @@ library GPv2Signing {
3878
/// trade.
3979
///
4080
/// @param recoveredOrder Memory location used for writing the recovered order data.
41-
/// @param domainSeparator The domain separator used for signing the order.
4281
/// @param tokens The list of tokens included in the settlement. The token
4382
/// indices in the trade parameters map to tokens in this array.
4483
/// @param trade The trade data to recover the order data from.
4584
function recoverOrderFromTrade(
4685
RecoveredOrder memory recoveredOrder,
47-
bytes32 domainSeparator,
4886
IERC20[] calldata tokens,
4987
GPv2Trade.Data calldata trade
5088
) internal view {
5189
GPv2Order.Data memory order = recoveredOrder.data;
5290

53-
GPv2Signing.Scheme signingScheme =
54-
GPv2Trade.extractOrder(trade, tokens, order);
91+
Scheme signingScheme = GPv2Trade.extractOrder(trade, tokens, order);
5592
(bytes32 orderDigest, address owner) =
56-
recoverOrderSigner(
57-
order,
58-
domainSeparator,
59-
signingScheme,
60-
trade.signature
61-
);
93+
recoverOrderSigner(order, signingScheme, trade.signature);
6294

6395
recoveredOrder.uid.packOrderUidParams(
6496
orderDigest,
@@ -74,36 +106,22 @@ library GPv2Signing {
74106
/// @dev Recovers an order's signer from the specified order and signature.
75107
///
76108
/// @param order The order to recover a signature for.
77-
/// @param domainSeparator The domain separator used for signing the order.
78109
/// @param signingScheme The signing scheme.
79110
/// @param signature The signature bytes.
80111
/// @return orderDigest The computed order hash.
81112
/// @return owner The recovered address from the specified signature.
82113
function recoverOrderSigner(
83114
GPv2Order.Data memory order,
84-
bytes32 domainSeparator,
85115
Scheme signingScheme,
86116
bytes calldata signature
87117
) internal view returns (bytes32 orderDigest, address owner) {
88118
orderDigest = order.hash();
89119
if (signingScheme == Scheme.Eip712) {
90-
owner = recoverEip712Signer(
91-
signature,
92-
domainSeparator,
93-
orderDigest
94-
);
120+
owner = recoverEip712Signer(signature, orderDigest);
95121
} else if (signingScheme == Scheme.EthSign) {
96-
owner = recoverEthsignSigner(
97-
signature,
98-
domainSeparator,
99-
orderDigest
100-
);
122+
owner = recoverEthsignSigner(signature, orderDigest);
101123
} else if (signingScheme == Scheme.Eip1271) {
102-
owner = recoverEip1271Signer(
103-
signature,
104-
domainSeparator,
105-
orderDigest
106-
);
124+
owner = recoverEip1271Signer(signature, orderDigest);
107125
}
108126
}
109127

@@ -169,15 +187,13 @@ library GPv2Signing {
169187
///
170188
/// @param encodedSignature Calldata pointing to tightly packed signature
171189
/// bytes.
172-
/// @param domainSeparator The domain separator used for signing the order.
173190
/// @param orderDigest The EIP-712 signing digest derived from the order
174191
/// parameters.
175192
/// @return owner The address of the signer.
176193
function recoverEip712Signer(
177194
bytes calldata encodedSignature,
178-
bytes32 domainSeparator,
179195
bytes32 orderDigest
180-
) internal pure returns (address owner) {
196+
) internal view returns (address owner) {
181197
(bytes32 r, bytes32 s, uint8 v) =
182198
decodeEcdsaSignature(encodedSignature);
183199

@@ -209,15 +225,13 @@ library GPv2Signing {
209225
///
210226
/// @param encodedSignature Calldata pointing to tightly packed signature
211227
/// bytes.
212-
/// @param domainSeparator The domain separator used for signing the order.
213228
/// @param orderDigest The EIP-712 signing digest derived from the order
214229
/// parameters.
215230
/// @return owner The address of the signer.
216231
function recoverEthsignSigner(
217232
bytes calldata encodedSignature,
218-
bytes32 domainSeparator,
219233
bytes32 orderDigest
220-
) internal pure returns (address owner) {
234+
) internal view returns (address owner) {
221235
(bytes32 r, bytes32 s, uint8 v) =
222236
decodeEcdsaSignature(encodedSignature);
223237

@@ -258,7 +272,6 @@ library GPv2Signing {
258272
/// cover the full length of the decoded signature.
259273
function recoverEip1271Signer(
260274
bytes calldata encodedSignature,
261-
bytes32 domainSeparator,
262275
bytes32 orderDigest
263276
) internal view returns (address owner) {
264277
// NOTE: Use assembly to read the verifier address from the encoded

src/contracts/test/GPv2SettlementTestInterface.sol

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ pragma abicoder v2;
44

55
import "../GPv2Settlement.sol";
66
import "../libraries/GPv2Interaction.sol";
7-
import "../libraries/GPv2Signing.sol";
87
import "../libraries/GPv2Trade.sol";
98
import "../libraries/GPv2TradeExecution.sol";
109

@@ -25,7 +24,7 @@ contract GPv2SettlementTestInterface is GPv2Settlement {
2524
}
2625

2726
function computeTradeExecutionMemoryTest() external returns (uint256 mem) {
28-
GPv2Signing.RecoveredOrder memory recoveredOrder;
27+
RecoveredOrder memory recoveredOrder;
2928
GPv2TradeExecution.Data memory executedTrade;
3029

3130
// NOTE: Solidity stores the free memory pointer at address 0x40. Read

src/contracts/test/GPv2SigningTestInterface.sol

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,31 +3,19 @@ pragma solidity ^0.7.6;
33
pragma abicoder v2;
44

55
import "../libraries/GPv2Order.sol";
6-
import "../libraries/GPv2Signing.sol";
76
import "../libraries/GPv2Trade.sol";
7+
import "../mixins/GPv2Signing.sol";
88

9-
contract GPv2SigningTestInterface {
10-
using GPv2Signing for GPv2Order.Data;
11-
using GPv2Signing for GPv2Signing.RecoveredOrder;
12-
13-
bytes32 public constant DOMAIN_SEPARATOR =
14-
keccak256(
15-
abi.encode(
16-
keccak256("EIP712Domain(string name)"),
17-
keccak256("test")
18-
)
19-
);
20-
9+
contract GPv2SigningTestInterface is GPv2Signing {
2110
function recoverOrderFromTradeTest(
2211
IERC20[] calldata tokens,
2312
GPv2Trade.Data calldata trade
2413
)
2514
external
2615
view
27-
returns (GPv2Signing.RecoveredOrder memory recoveredOrder, uint256 mem)
16+
returns (RecoveredOrder memory recoveredOrder, uint256 mem)
2817
{
29-
bytes32 domainSeparator = DOMAIN_SEPARATOR;
30-
recoveredOrder = GPv2Signing.allocateRecoveredOrder();
18+
recoveredOrder = allocateRecoveredOrder();
3119

3220
// NOTE: Solidity stores the free memory pointer at address 0x40. Read
3321
// it before and after calling `processOrder` to ensure that there are
@@ -37,7 +25,7 @@ contract GPv2SigningTestInterface {
3725
mem := mload(0x40)
3826
}
3927

40-
recoveredOrder.recoverOrderFromTrade(domainSeparator, tokens, trade);
28+
recoverOrderFromTrade(recoveredOrder, tokens, trade);
4129

4230
// solhint-disable-next-line no-inline-assembly
4331
assembly {
@@ -50,10 +38,6 @@ contract GPv2SigningTestInterface {
5038
GPv2Signing.Scheme signingScheme,
5139
bytes calldata signature
5240
) external view returns (address owner) {
53-
(, owner) = order.recoverOrderSigner(
54-
DOMAIN_SEPARATOR,
55-
signingScheme,
56-
signature
57-
);
41+
(, owner) = recoverOrderSigner(order, signingScheme, signature);
5842
}
5943
}

test/GPv2Settlement.test.ts

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -52,26 +52,6 @@ describe("GPv2Settlement", () => {
5252
testDomain = domain(chainId, settlement.address);
5353
});
5454

55-
describe("domainSeparator", () => {
56-
it("should have an EIP-712 domain separator", async () => {
57-
expect(await settlement.domainSeparator()).to.equal(
58-
ethers.utils._TypedDataEncoder.hashDomain(testDomain),
59-
);
60-
});
61-
62-
it("should have a different replay protection for each deployment", async () => {
63-
const GPv2Settlement = await ethers.getContractFactory(
64-
"GPv2SettlementTestInterface",
65-
deployer,
66-
);
67-
const settlement2 = await GPv2Settlement.deploy(authenticator.address);
68-
69-
expect(await settlement.domainSeparator()).to.not.equal(
70-
await settlement2.domainSeparator(),
71-
);
72-
});
73-
});
74-
7555
describe("authenticator", () => {
7656
it("should be set to the authenticator the contract was initialized with", async () => {
7757
expect(await settlement.authenticator()).to.equal(authenticator.address);

0 commit comments

Comments
 (0)