From 407ea51d6a8827e38f250f0e1ad455d3fa2b6486 Mon Sep 17 00:00:00 2001 From: hueso Date: Sat, 2 Aug 2025 20:03:46 -0300 Subject: [PATCH] WIP: use openzeppelin contracts --- contracts/core/BaseUtils.sol | 7 +- contracts/core/DataTypes.sol | 2 +- contracts/core/EventAndErrors.sol | 2 +- contracts/lib/mock/mockToken.sol | 4 +- contracts/lib/tokens/ERC20.sol | 250 ------------------------ contracts/lib/utils/MerkleProofLib.sol | 58 ------ contracts/lib/utils/ReentrancyGuard.sol | 34 ---- contracts/lib/utils/SafeTransferLib.sol | 2 +- package.json | 1 + yarn.lock | 8 + 10 files changed, 18 insertions(+), 350 deletions(-) delete mode 100644 contracts/lib/tokens/ERC20.sol delete mode 100644 contracts/lib/utils/MerkleProofLib.sol delete mode 100644 contracts/lib/utils/ReentrancyGuard.sol diff --git a/contracts/core/BaseUtils.sol b/contracts/core/BaseUtils.sol index eddacf5..3cc8c78 100644 --- a/contracts/core/BaseUtils.sol +++ b/contracts/core/BaseUtils.sol @@ -4,8 +4,9 @@ pragma solidity ^0.8.19; import { ERC20, OwnerSettings } from "contracts/core/OwnerSettings.sol"; import { ECDSA } from "contracts/lib/utils/ECDSA.sol"; -import { MerkleProofLib as Merkle } from "contracts/lib/utils/MerkleProofLib.sol"; -import { ReentrancyGuard } from "contracts/lib/utils/ReentrancyGuard.sol"; +import { MessageHashUtils } from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; +import { MerkleProof as Merkle } from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol"; +import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; abstract contract BaseUtils is OwnerSettings, @@ -44,7 +45,7 @@ abstract contract BaseUtils is !validBacenSigners( _castAddrToKey( ECDSA.recoverCalldata( - ECDSA.toEthSignedMessageHash( + MessageHashUtils.toEthSignedMessageHash( _message ), _signature diff --git a/contracts/core/DataTypes.sol b/contracts/core/DataTypes.sol index f7b901b..eb784e0 100644 --- a/contracts/core/DataTypes.sol +++ b/contracts/core/DataTypes.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.19; -import { ERC20 } from "contracts/lib/tokens/ERC20.sol"; +import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; library DataTypes { diff --git a/contracts/core/EventAndErrors.sol b/contracts/core/EventAndErrors.sol index 918e9b8..99ec2ac 100644 --- a/contracts/core/EventAndErrors.sol +++ b/contracts/core/EventAndErrors.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.19; -import { ERC20 } from "contracts/lib/tokens/ERC20.sol"; +import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; // prettier-ignore interface EventAndErrors { diff --git a/contracts/lib/mock/mockToken.sol b/contracts/lib/mock/mockToken.sol index f854139..debe110 100644 --- a/contracts/lib/mock/mockToken.sol +++ b/contracts/lib/mock/mockToken.sol @@ -1,10 +1,10 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.19; -import { ERC20 } from "../tokens/ERC20.sol"; +import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract MockToken is ERC20 { - constructor(uint256 supply) ERC20("MockBRL", "MBRL", 18) { + constructor(uint256 supply) ERC20("MockBRL", "MBRL") { _mint(msg.sender, supply); } diff --git a/contracts/lib/tokens/ERC20.sol b/contracts/lib/tokens/ERC20.sol deleted file mode 100644 index bb4a7db..0000000 --- a/contracts/lib/tokens/ERC20.sol +++ /dev/null @@ -1,250 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.8.4; - -/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation. -/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC20.sol) -/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol) -/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it. -abstract contract ERC20 { - /*////////////////////////////////////////////////////////////// - EVENTS - //////////////////////////////////////////////////////////////*/ - - event Transfer( - address indexed from, - address indexed to, - uint256 amount - ); - - event Approval( - address indexed owner, - address indexed spender, - uint256 amount - ); - - /*////////////////////////////////////////////////////////////// - METADATA STORAGE - //////////////////////////////////////////////////////////////*/ - - string public name; - - string public symbol; - - uint8 public immutable decimals; - - /*////////////////////////////////////////////////////////////// - ERC20 STORAGE - //////////////////////////////////////////////////////////////*/ - - uint256 public totalSupply; - - mapping(address => uint256) public balanceOf; - - mapping(address => mapping(address => uint256)) - public allowance; - - /*////////////////////////////////////////////////////////////// - EIP-2612 STORAGE - //////////////////////////////////////////////////////////////*/ - - uint256 internal immutable INITIAL_CHAIN_ID; - - bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR; - - mapping(address => uint256) public nonces; - - /*////////////////////////////////////////////////////////////// - CONSTRUCTOR - //////////////////////////////////////////////////////////////*/ - - constructor( - string memory _name, - string memory _symbol, - uint8 _decimals - ) { - name = _name; - symbol = _symbol; - decimals = _decimals; - - INITIAL_CHAIN_ID = block.chainid; - INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator(); - } - - /*////////////////////////////////////////////////////////////// - ERC20 LOGIC - //////////////////////////////////////////////////////////////*/ - - function approve( - address spender, - uint256 amount - ) public virtual returns (bool) { - allowance[msg.sender][spender] = amount; - - emit Approval(msg.sender, spender, amount); - - return true; - } - - function transfer( - address to, - uint256 amount - ) public virtual returns (bool) { - balanceOf[msg.sender] -= amount; - - // Cannot overflow because the sum of all user - // balances can't exceed the max uint256 value. - unchecked { - balanceOf[to] += amount; - } - - emit Transfer(msg.sender, to, amount); - - return true; - } - - function transferFrom( - address from, - address to, - uint256 amount - ) public virtual returns (bool) { - uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals. - - if (allowed != type(uint256).max) - allowance[from][msg.sender] = allowed - amount; - - balanceOf[from] -= amount; - - // Cannot overflow because the sum of all user - // balances can't exceed the max uint256 value. - unchecked { - balanceOf[to] += amount; - } - - emit Transfer(from, to, amount); - - return true; - } - - /*////////////////////////////////////////////////////////////// - EIP-2612 LOGIC - //////////////////////////////////////////////////////////////*/ - - function permit( - address owner, - address spender, - uint256 value, - uint256 deadline, - uint8 v, - bytes32 r, - bytes32 s - ) public virtual { - require( - deadline >= block.timestamp, - "PERMIT_DEADLINE_EXPIRED" - ); - - // Unchecked because the only math done is incrementing - // the owner's nonce which cannot realistically overflow. - unchecked { - address recoveredAddress = ecrecover( - keccak256( - abi.encodePacked( - "\x19\x01", - DOMAIN_SEPARATOR(), - keccak256( - abi.encode( - keccak256( - "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" - ), - owner, - spender, - value, - nonces[owner]++, - deadline - ) - ) - ) - ), - v, - r, - s - ); - - require( - recoveredAddress != address(0) && - recoveredAddress == owner, - "INVALID_SIGNER" - ); - - allowance[recoveredAddress][spender] = value; - } - - emit Approval(owner, spender, value); - } - - function DOMAIN_SEPARATOR() - public - view - virtual - returns (bytes32) - { - return - block.chainid == INITIAL_CHAIN_ID - ? INITIAL_DOMAIN_SEPARATOR - : computeDomainSeparator(); - } - - function computeDomainSeparator() - internal - view - virtual - returns (bytes32) - { - return - keccak256( - abi.encode( - keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ), - keccak256(bytes(name)), - keccak256("1"), - block.chainid, - address(this) - ) - ); - } - - /*////////////////////////////////////////////////////////////// - INTERNAL MINT/BURN LOGIC - //////////////////////////////////////////////////////////////*/ - - function _mint( - address to, - uint256 amount - ) internal virtual { - totalSupply += amount; - - // Cannot overflow because the sum of all user - // balances can't exceed the max uint256 value. - unchecked { - balanceOf[to] += amount; - } - - emit Transfer(address(0), to, amount); - } - - function _burn( - address from, - uint256 amount - ) internal virtual { - balanceOf[from] -= amount; - - // Cannot underflow because a user's balance - // will never be larger than the total supply. - unchecked { - totalSupply -= amount; - } - - emit Transfer(from, address(0), amount); - } -} diff --git a/contracts/lib/utils/MerkleProofLib.sol b/contracts/lib/utils/MerkleProofLib.sol deleted file mode 100644 index 2041b0e..0000000 --- a/contracts/lib/utils/MerkleProofLib.sol +++ /dev/null @@ -1,58 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.8.4; - -/// @notice Gas optimized verification of proof of inclusion for a leaf in a Merkle tree. -/// @author Solady -/// (https://github.com/vectorized/solady/blob/main/src/utils/MerkleProofLib.sol) -/// @author Modified from Solmate -/// (https://github.com/transmissions11/solmate/blob/main/src/utils/MerkleProofLib.sol) -/// @author Modified from OpenZeppelin -/// (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/MerkleProof.sol) -library MerkleProofLib { - /// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`. - function verify( - bytes32[] calldata proof, - bytes32 root, - bytes32 leaf - ) internal pure returns (bool isValid) { - /// @solidity memory-safe-assembly - assembly { - if proof.length { - // Left shift by 5 is equivalent to multiplying by 0x20. - let end := add( - proof.offset, - shl(5, proof.length) - ) - // Initialize `offset` to the offset of `proof` in the calldata. - let offset := proof.offset - // Iterate over proof elements to compute root hash. - for { - - } 1 { - - } { - // Slot of `leaf` in scratch space. - // If the condition is true: 0x20, otherwise: 0x00. - let scratch := shl( - 5, - gt(leaf, calldataload(offset)) - ) - // Store elements to hash contiguously in scratch space. - // Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes. - mstore(scratch, leaf) - mstore( - xor(scratch, 0x20), - calldataload(offset) - ) - // Reuse `leaf` to store the hash to reduce stack operations. - leaf := keccak256(0x00, 0x40) - offset := add(offset, 0x20) - if iszero(lt(offset, end)) { - break - } - } - } - isValid := eq(leaf, root) - } - } -} diff --git a/contracts/lib/utils/ReentrancyGuard.sol b/contracts/lib/utils/ReentrancyGuard.sol deleted file mode 100644 index 24607ad..0000000 --- a/contracts/lib/utils/ReentrancyGuard.sol +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.8.4; - -/// @notice Reentrancy protection for smart contracts. -/// @author z0r0z.eth -/// @author Modified from Seaport -/// (https://github.com/ProjectOpenSea/seaport/blob/main/contracts/lib/ReentrancyGuard.sol) -/// @author Modified from Solmate -/// (https://github.com/Rari-Capital/solmate/blob/main/src/utils/ReentrancyGuard.sol) -abstract contract ReentrancyGuard { - error Reentrancy(); - - uint256 private guard = 1; - - modifier nonReentrant() virtual { - setReentrancyGuard(); - - _; - - clearReentrancyGuard(); - } - - /// @dev Check guard sentinel value and set it. - function setReentrancyGuard() internal virtual { - if (guard == 2) revert Reentrancy(); - - guard = 2; - } - - /// @dev Unset sentinel value. - function clearReentrancyGuard() internal virtual { - guard = 1; - } -} diff --git a/contracts/lib/utils/SafeTransferLib.sol b/contracts/lib/utils/SafeTransferLib.sol index 9697682..a702ca5 100644 --- a/contracts/lib/utils/SafeTransferLib.sol +++ b/contracts/lib/utils/SafeTransferLib.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.4; -import { ERC20 } from "../tokens/ERC20.sol"; +import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol) diff --git a/package.json b/package.json index a4b3eff..23cca9c 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "@nomicfoundation/hardhat-verify": "^2.1.0", "@nomicfoundation/hardhat-viem": "^2.1.0", "@nomicfoundation/ignition-core": "^0.15.13", + "@openzeppelin/contracts": "^5.4.0", "@typechain/ethers-v6": "^0.5.1", "@typechain/hardhat": "^9.1.0", "@types/chai": "^4.3.20", diff --git a/yarn.lock b/yarn.lock index e3ef432..37d5f23 100644 --- a/yarn.lock +++ b/yarn.lock @@ -988,6 +988,13 @@ __metadata: languageName: node linkType: hard +"@openzeppelin/contracts@npm:^5.4.0": + version: 5.4.0 + resolution: "@openzeppelin/contracts@npm:5.4.0" + checksum: 10/4f7f926ebd98279ba8223cae5d2cd38e0806e60e3e3a615c9d739ffab870ffab7a1e85f47092faa123c0f26d1699ca15252adea8bcdcaf360b21781acc93b218 + languageName: node + linkType: hard + "@pkgjs/parseargs@npm:^0.11.0": version: 0.11.0 resolution: "@pkgjs/parseargs@npm:0.11.0" @@ -4563,6 +4570,7 @@ __metadata: "@nomicfoundation/hardhat-verify": "npm:^2.1.0" "@nomicfoundation/hardhat-viem": "npm:^2.1.0" "@nomicfoundation/ignition-core": "npm:^0.15.13" + "@openzeppelin/contracts": "npm:^5.4.0" "@typechain/ethers-v6": "npm:^0.5.1" "@typechain/hardhat": "npm:^9.1.0" "@types/chai": "npm:^4.3.20"