or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

access-control.mdcontract-presets.mdcryptography-math.mdgsn-support.mdindex.mdintrospection.mdpayment-mechanisms.mdproxy-patterns.mdsecurity-utilities.mdtoken-standards.md

cryptography-math.mddocs/

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

```