0
# Cryptography & Math
1
2
OpenZeppelin provides cryptographic utilities and mathematical libraries for signature verification, hash operations, Merkle tree validation, and safe arithmetic operations essential for secure smart contract development.
3
4
## Capabilities
5
6
### ECDSA - Digital Signature Operations
7
8
Library for verifying Elliptic Curve Digital Signature Algorithm (ECDSA) signatures, commonly used for message signing and verification.
9
10
```solidity { .api }
11
/**
12
* Elliptic Curve Digital Signature Algorithm (ECDSA) operations
13
*/
14
library ECDSA {
15
enum RecoverError {
16
NoError,
17
InvalidSignature,
18
InvalidSignatureLength,
19
InvalidSignatureS,
20
InvalidSignatureV
21
}
22
23
/**
24
* Returns the address that signed a hashed message (hash) with signature
25
* This address can then be used for verification purposes
26
*/
27
function recover(bytes32 hash, bytes memory signature) internal pure returns (address);
28
29
/**
30
* Overload of recover that receives the r, s and v signature fields separately
31
*/
32
function recover(
33
bytes32 hash,
34
uint8 v,
35
bytes32 r,
36
bytes32 s
37
) internal pure returns (address);
38
39
/**
40
* Overload of recover that receives the r, s and v signature fields separately
41
* Returns a RecoverError in case of failure
42
*/
43
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError);
44
45
/**
46
* Overload of tryRecover with r, s, v signature fields
47
*/
48
function tryRecover(
49
bytes32 hash,
50
uint8 v,
51
bytes32 r,
52
bytes32 s
53
) internal pure returns (address, RecoverError);
54
55
/**
56
* Returns an Ethereum Signed Message, created from a hash
57
* This produces a hash corresponding to the one signed with the JSON-RPC eth_sign method
58
*/
59
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32);
60
61
/**
62
* Returns an Ethereum Signed Message, created from s
63
* This produces a hash corresponding to the one signed with the JSON-RPC eth_sign method
64
*/
65
function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32);
66
67
/**
68
* Returns an Ethereum Signed Typed Data, created from a domainSeparator and a structHash
69
* This produces a hash corresponding to the one signed with the JSON-RPC eth_signTypedData method
70
*/
71
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32);
72
}
73
```
74
75
**Usage Examples:**
76
77
```solidity
78
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
79
80
contract SignatureVerifier {
81
using ECDSA for bytes32;
82
83
function verifySignature(
84
bytes32 message,
85
bytes memory signature,
86
address expectedSigner
87
) public pure returns (bool) {
88
bytes32 ethSignedMessageHash = message.toEthSignedMessageHash();
89
address recoveredSigner = ethSignedMessageHash.recover(signature);
90
return recoveredSigner == expectedSigner;
91
}
92
93
function verifyMessageSigner(
94
string memory message,
95
bytes memory signature,
96
address expectedSigner
97
) public pure returns (bool) {
98
bytes32 messageHash = keccak256(abi.encodePacked(message));
99
return verifySignature(messageHash, signature, expectedSigner);
100
}
101
}
102
```
103
104
### MerkleProof - Merkle Tree Verification
105
106
Library for verifying Merkle tree proofs, commonly used for efficient verification of large datasets.
107
108
```solidity { .api }
109
/**
110
* Library for dealing with merkle proofs
111
*/
112
library MerkleProof {
113
/**
114
* Returns true if a leaf can be proved to be part of a Merkle tree defined by root
115
* For this, a proof must be provided, containing sibling hashes on the branch from the leaf to the root
116
*/
117
function verify(
118
bytes32[] memory proof,
119
bytes32 root,
120
bytes32 leaf
121
) internal pure returns (bool);
122
123
/**
124
* Calldata version of verify
125
*/
126
function verifyCalldata(
127
bytes32[] calldata proof,
128
bytes32 root,
129
bytes32 leaf
130
) internal pure returns (bool);
131
132
/**
133
* Returns the root of a tree reconstructed from leaves and the given proof
134
*/
135
function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32);
136
137
/**
138
* Calldata version of processProof
139
*/
140
function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32);
141
}
142
```
143
144
**Usage Examples:**
145
146
```solidity
147
import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
148
149
contract MerkleWhitelist {
150
using MerkleProof for bytes32[];
151
152
bytes32 public immutable merkleRoot;
153
mapping(address => bool) public claimed;
154
155
constructor(bytes32 _merkleRoot) {
156
merkleRoot = _merkleRoot;
157
}
158
159
function claimTokens(
160
uint256 amount,
161
bytes32[] calldata merkleProof
162
) external {
163
require(!claimed[msg.sender], "Already claimed");
164
165
bytes32 leaf = keccak256(abi.encodePacked(msg.sender, amount));
166
require(
167
MerkleProof.verify(merkleProof, merkleRoot, leaf),
168
"Invalid merkle proof"
169
);
170
171
claimed[msg.sender] = true;
172
// Transfer tokens to msg.sender
173
}
174
}
175
```
176
177
### EIP712 - Typed Data Signing
178
179
Implementation of EIP-712 for structured data hashing and signing.
180
181
```solidity { .api }
182
/**
183
* Implementation of the EIP712 domain separator
184
*/
185
abstract contract EIP712 {
186
/**
187
* Returns the domain separator for the current chain
188
*/
189
function _domainSeparatorV4() internal view returns (bytes32);
190
191
/**
192
* Given an already hashed struct, this function returns the hash of the fully encoded EIP712 message for this domain
193
*/
194
function _hashTypedDataV4(bytes32 structHash) internal view returns (bytes32);
195
196
/**
197
* The hash of the name parameter for the EIP712 domain
198
*/
199
function _EIP712NameHash() internal virtual view returns (bytes32);
200
201
/**
202
* The hash of the version parameter for the EIP712 domain
203
*/
204
function _EIP712VersionHash() internal virtual view returns (bytes32);
205
}
206
```
207
208
**Usage Example:**
209
210
```solidity
211
import "@openzeppelin/contracts/utils/cryptography/draft-EIP712.sol";
212
213
contract VoteContract is EIP712 {
214
bytes32 private constant VOTE_TYPEHASH =
215
keccak256("Vote(address voter,uint256 proposalId,bool support)");
216
217
mapping(bytes32 => bool) public usedSignatures;
218
219
constructor() EIP712("VoteContract", "1") {}
220
221
function voteWithSignature(
222
address voter,
223
uint256 proposalId,
224
bool support,
225
uint8 v,
226
bytes32 r,
227
bytes32 s
228
) external {
229
bytes32 structHash = keccak256(abi.encode(
230
VOTE_TYPEHASH,
231
voter,
232
proposalId,
233
support
234
));
235
236
bytes32 hash = _hashTypedDataV4(structHash);
237
address signer = ECDSA.recover(hash, v, r, s);
238
239
require(signer == voter, "Invalid signature");
240
require(!usedSignatures[hash], "Signature already used");
241
242
usedSignatures[hash] = true;
243
// Process vote
244
}
245
}
246
```
247
248
### ERC20Permit - Gasless Approvals
249
250
Extension of ERC20 that allows approvals to be made via signatures, enabling gasless transactions.
251
252
```solidity { .api }
253
/**
254
* Interface of the ERC20 Permit extension allowing approvals via signatures
255
*/
256
interface IERC20Permit {
257
/**
258
* Sets value as the allowance of spender over owner's tokens, given owner's signed approval
259
*/
260
function permit(
261
address owner,
262
address spender,
263
uint256 value,
264
uint256 deadline,
265
uint8 v,
266
bytes32 r,
267
bytes32 s
268
) external;
269
270
/**
271
* Returns the current nonce for owner
272
*/
273
function nonces(address owner) external view returns (uint256);
274
275
/**
276
* Returns the domain separator used in the encoding of the signature for permit
277
*/
278
function DOMAIN_SEPARATOR() external view returns (bytes32);
279
}
280
281
/**
282
* Abstract contract implementation of the ERC20 Permit extension
283
*/
284
abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 {
285
mapping(address => Counters.Counter) private _nonces;
286
287
bytes32 private constant _PERMIT_TYPEHASH =
288
keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
289
290
/**
291
* See IERC20Permit.permit
292
*/
293
function permit(
294
address owner,
295
address spender,
296
uint256 value,
297
uint256 deadline,
298
uint8 v,
299
bytes32 r,
300
bytes32 s
301
) public virtual override;
302
303
/**
304
* See IERC20Permit.nonces
305
*/
306
function nonces(address owner) public view virtual override returns (uint256);
307
308
/**
309
* See IERC20Permit.DOMAIN_SEPARATOR
310
*/
311
function DOMAIN_SEPARATOR() external view override returns (bytes32);
312
}
313
```
314
315
### Create2 - Deterministic Deployment
316
317
Library for deploying contracts using CREATE2 opcode for deterministic addresses.
318
319
```solidity { .api }
320
/**
321
* Helper to make usage of the CREATE2 EVM opcode easier and safer
322
*/
323
library Create2 {
324
/**
325
* Deploys a contract using CREATE2. The address where the contract will be deployed can be known in advance
326
*/
327
function deploy(
328
uint256 amount,
329
bytes32 salt,
330
bytes memory bytecode
331
) internal returns (address);
332
333
/**
334
* Returns the address where a contract will be stored if deployed via deploy
335
*/
336
function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address);
337
338
/**
339
* Returns the address where a contract will be stored if deployed via deploy from a deployer
340
*/
341
function computeAddress(
342
bytes32 salt,
343
bytes32 bytecodeHash,
344
address deployer
345
) internal pure returns (address);
346
}
347
```
348
349
**Usage Example:**
350
351
```solidity
352
import "@openzeppelin/contracts/utils/Create2.sol";
353
354
contract TokenFactory {
355
event TokenCreated(address indexed token, bytes32 indexed salt);
356
357
function createToken(
358
string memory name,
359
string memory symbol,
360
uint256 supply,
361
bytes32 salt
362
) external returns (address) {
363
bytes memory bytecode = abi.encodePacked(
364
type(MyToken).creationCode,
365
abi.encode(name, symbol, supply)
366
);
367
368
address token = Create2.deploy(0, salt, bytecode);
369
emit TokenCreated(token, salt);
370
return token;
371
}
372
373
function predictTokenAddress(
374
string memory name,
375
string memory symbol,
376
uint256 supply,
377
bytes32 salt
378
) external view returns (address) {
379
bytes memory bytecode = abi.encodePacked(
380
type(MyToken).creationCode,
381
abi.encode(name, symbol, supply)
382
);
383
384
return Create2.computeAddress(salt, keccak256(bytecode));
385
}
386
}
387
```
388
389
## Advanced Cryptographic Patterns
390
391
### Multi-Signature Verification
392
393
```solidity
394
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
395
396
contract MultiSigWallet {
397
using ECDSA for bytes32;
398
399
address[] public owners;
400
uint256 public required;
401
mapping(address => bool) public isOwner;
402
403
struct Transaction {
404
address to;
405
uint256 value;
406
bytes data;
407
bool executed;
408
uint256 nonce;
409
}
410
411
mapping(bytes32 => uint256) public confirmations;
412
mapping(bytes32 => mapping(address => bool)) public hasConfirmed;
413
414
constructor(address[] memory _owners, uint256 _required) {
415
require(_owners.length >= _required && _required > 0, "Invalid requirements");
416
417
for (uint i = 0; i < _owners.length; i++) {
418
require(_owners[i] != address(0) && !isOwner[_owners[i]], "Invalid owner");
419
isOwner[_owners[i]] = true;
420
}
421
422
owners = _owners;
423
required = _required;
424
}
425
426
function executeTransaction(
427
Transaction memory txn,
428
bytes[] memory signatures
429
) external {
430
require(signatures.length >= required, "Not enough signatures");
431
432
bytes32 txnHash = keccak256(abi.encode(txn));
433
bytes32 ethSignedTxnHash = txnHash.toEthSignedMessageHash();
434
435
address[] memory signers = new address[](signatures.length);
436
437
for (uint i = 0; i < signatures.length; i++) {
438
address signer = ethSignedTxnHash.recover(signatures[i]);
439
require(isOwner[signer], "Invalid signer");
440
441
// Check for duplicate signers
442
for (uint j = 0; j < i; j++) {
443
require(signers[j] != signer, "Duplicate signature");
444
}
445
signers[i] = signer;
446
}
447
448
require(!txn.executed, "Transaction already executed");
449
450
// Execute transaction
451
(bool success, ) = txn.to.call{value: txn.value}(txn.data);
452
require(success, "Transaction failed");
453
}
454
}
455
```
456
457
### Commit-Reveal Scheme
458
459
```solidity
460
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
461
462
contract CommitRevealVoting {
463
using ECDSA for bytes32;
464
465
struct Vote {
466
bytes32 commitment;
467
uint256 choice;
468
bool revealed;
469
}
470
471
mapping(address => Vote) public votes;
472
mapping(uint256 => uint256) public voteCounts;
473
474
uint256 public commitPhaseEnd;
475
uint256 public revealPhaseEnd;
476
477
constructor(uint256 commitDuration, uint256 revealDuration) {
478
commitPhaseEnd = block.timestamp + commitDuration;
479
revealPhaseEnd = commitPhaseEnd + revealDuration;
480
}
481
482
function commitVote(bytes32 commitment) external {
483
require(block.timestamp < commitPhaseEnd, "Commit phase ended");
484
votes[msg.sender].commitment = commitment;
485
}
486
487
function revealVote(uint256 choice, uint256 nonce) external {
488
require(block.timestamp >= commitPhaseEnd, "Commit phase not ended");
489
require(block.timestamp < revealPhaseEnd, "Reveal phase ended");
490
491
Vote storage vote = votes[msg.sender];
492
require(vote.commitment != bytes32(0), "No commitment found");
493
require(!vote.revealed, "Already revealed");
494
495
bytes32 hash = keccak256(abi.encodePacked(choice, nonce, msg.sender));
496
require(hash == vote.commitment, "Invalid reveal");
497
498
vote.choice = choice;
499
vote.revealed = true;
500
voteCounts[choice]++;
501
}
502
}
503
```
504
505
### Merkle Airdrop with Batching
506
507
```solidity
508
import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
509
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
510
511
contract MerkleAirdrop {
512
IERC20 public immutable token;
513
bytes32 public immutable merkleRoot;
514
515
mapping(address => bool) public claimed;
516
517
event Claimed(address indexed account, uint256 amount);
518
519
constructor(IERC20 _token, bytes32 _merkleRoot) {
520
token = _token;
521
merkleRoot = _merkleRoot;
522
}
523
524
function claim(
525
uint256 amount,
526
bytes32[] calldata merkleProof
527
) external {
528
require(!claimed[msg.sender], "Already claimed");
529
530
bytes32 leaf = keccak256(abi.encodePacked(msg.sender, amount));
531
require(MerkleProof.verify(merkleProof, merkleRoot, leaf), "Invalid proof");
532
533
claimed[msg.sender] = true;
534
require(token.transfer(msg.sender, amount), "Transfer failed");
535
536
emit Claimed(msg.sender, amount);
537
}
538
539
function claimMultiple(
540
address[] calldata recipients,
541
uint256[] calldata amounts,
542
bytes32[][] calldata merkleProofs
543
) external {
544
require(recipients.length == amounts.length, "Array length mismatch");
545
require(recipients.length == merkleProofs.length, "Array length mismatch");
546
547
for (uint256 i = 0; i < recipients.length; i++) {
548
address recipient = recipients[i];
549
uint256 amount = amounts[i];
550
551
if (!claimed[recipient]) {
552
bytes32 leaf = keccak256(abi.encodePacked(recipient, amount));
553
554
if (MerkleProof.verify(merkleProofs[i], merkleRoot, leaf)) {
555
claimed[recipient] = true;
556
require(token.transfer(recipient, amount), "Transfer failed");
557
emit Claimed(recipient, amount);
558
}
559
}
560
}
561
}
562
}
563
```