### Install Upgradeable Contracts Package Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/docs/modules/ROOT/pages/upgradeable.adoc Install the upgradeable contracts package and the main contracts package as a peer dependency. ```bash $ npm install @openzeppelin/contracts-upgradeable @openzeppelin/contracts ``` -------------------------------- ### Query and Transfer Tokens Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/docs/modules/ROOT/pages/erc20.adoc JavaScript console examples for checking balances and transferring tokens after deployment. ```javascript > GLDToken.balanceOf(deployerAddress) 1000000000000000000000 ``` ```javascript > GLDToken.transfer(otherAddress, 300000000000000000000) > GLDToken.balanceOf(otherAddress) 300000000000000000000 > GLDToken.balanceOf(deployerAddress) 700000000000000000000 ``` -------------------------------- ### Run specific configuration Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/fv/README.md Example command to execute the AccessControl configuration. ```bash node fv/run.js AccessControl # Run the AccessControl configuration (fv/specs/AccessControl.conf) using its harness and spec ``` -------------------------------- ### Install OpenZeppelin Contracts with npm Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/README.md Installs the latest audited release of OpenZeppelin Contracts using npm. This is the default and recommended installation method for Hardhat projects. ```bash npm install @openzeppelin/contracts ``` -------------------------------- ### Install OpenZeppelin Contracts with Forge Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/README.md Installs OpenZeppelin Contracts using the Forge package manager. Ensure you are installing a tagged release and not the 'master' branch for stability and security. ```bash forge install OpenZeppelin/openzeppelin-contracts ``` -------------------------------- ### PaymasterUSDCGuaranteed Contract Example Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/docs/modules/ROOT/pages/paymasters.adoc An example implementation of a guarantor-enabled paymaster, extending `PaymasterERC20Guarantor`. It includes logic to extract and validate the guarantor's signature from the `paymasterData`. ```Solidity // WARNING: Unaudited code. // Consider performing a security review before going to production. contract PaymasterUSDCGuaranteed is EIP712, PaymasterERC20Guarantor, Ownable { // Keep the same oracle code as before... bytes32 private constant GUARANTEED_USER_OPERATION_TYPEHASH = keccak256( "GuaranteedUserOperation(address sender,uint256 nonce,bytes initCode,bytes callData,bytes32 accountGasLimits,uint256 preVerificationGas,bytes32 gasFees,bytes paymasterData)" ); constructor( address initialOwner ) EIP712("PaymasterUSDCGuaranteed", "1") Ownable(initialOwner) {} // Other functions from PaymasterUSDCChainlink... function _fetchGuarantor( PackedUserOperation calldata userOp ) internal view override returns (address guarantor) { bytes calldata paymasterData = userOp.paymasterData(); // If no guarantor specified, return early if (paymasterData.length < 20) { return address(0); } guarantor = address(bytes20(paymasterData)); bytes calldata guarantorSignature = paymasterData[20:]; // Validate the guarantor's signature bytes32 structHash = _getGuaranteedOperationStructHash(userOp); bytes32 hash = _hashTypedDataV4(structHash); return SignatureChecker.isValidSignatureNow( guarantor, hash, guarantorSignature ) ? guarantor : address(0); } function _getGuaranteedOperationStructHash( PackedUserOperation calldata userOp ) internal pure returns (bytes32) { return keccak256( abi.encode( GUARANTEED_USER_OPERATION_TYPEHASH, userOp.sender, userOp.nonce, keccak256(userOp.initCode), keccak256(userOp.callData), userOp.accountGasLimits, userOp.preVerificationGas, userOp.gasFees, keccak256(bytes(userOp.paymasterData()[:20])) // Just the guarantor address part ) ); } } ``` -------------------------------- ### Setup Standard Multisig Account Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/docs/modules/ROOT/pages/multisig.adoc Demonstrates initializing a 2-of-3 multisig account with ECDSA, P256, and RSA signers. Ensure signers are correctly formatted and the threshold is set appropriately. ```solidity // Example setup code function setupMultisigAccount() external { // Create signers using different types of keys bytes memory ecdsaSigner = alice; // EOA address (20 bytes) // P256 signer with format: verifier || pubKey bytes memory p256Signer = abi.encodePacked( p256Verifier, bobP256PublicKeyX, bobP256PublicKeyY ); // RSA signer with format: verifier || pubKey bytes memory rsaSigner = abi.encodePacked( rsaVerifier, abi.encode(charlieRSAPublicKeyE, charlieRSAPublicKeyN) ); // Create array of signers bytes[] memory signers = new bytes[](3); signers[0] = ecdsaSigner; signers[1] = p256Signer; signers[2] = rsaSigner; // Set threshold to 2 (2-of-3 multisig) uint256 threshold = 2; // Initialize the account myMultisigAccount.initialize(signers, threshold); } ``` -------------------------------- ### Import and Use OpenZeppelin Contracts Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/docs/modules/ROOT/pages/index.adoc Demonstrates how to import and use contracts from the OpenZeppelin library within your Solidity project. Ensure contracts are installed correctly before importing. ```solidity include::api:example$MyNFT.sol[] ``` -------------------------------- ### Configure Foundry Remappings Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/README.md Sets up the necessary remappings in `remappings.txt` for Forge to correctly resolve OpenZeppelin Contracts when installed via git. ```plaintext @openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/ ``` -------------------------------- ### MyFactoryAccount Solidity Contract Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/docs/modules/ROOT/pages/accounts.adoc Example of a custom account factory contract using the Clones library for deterministic account creation. ```solidity import { Clones } from "@openzeppelin/contracts/proxy/Clones.sol"; import { Account } from "@openzeppelin/contracts/account/Account.sol"; contract MyFactoryAccount is Account { constructor() Account(msg.sender) {} function createAccount(bytes32 salt, address owner) public returns (address) { return Clones.predictDeterministicAddress(address(this), salt, address(this)); } function deployAccount(bytes32 salt, address owner) public { address account = createAccount(salt, owner); // The account is deployed by the factory, so the factory is the one that needs to be authorized // to deploy the account. This is done by passing the factory's address as the owner. Account newAccount = Account.at(account); newAccount.initialize(owner); } } ``` -------------------------------- ### Install Latest Unaudited Release with npm Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/README.md Installs the latest unaudited release of OpenZeppelin Contracts using npm. Use this for the 'dev' tag when you need the most recent features before they are audited. ```bash npm install @openzeppelin/contracts@dev ``` -------------------------------- ### Initialize ECDSA Account Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/docs/modules/ROOT/pages/accounts.adoc Example of initializing an ECDSA-based smart account. This is typically done by a factory contract right after deployment. ```Solidity import {Account} from "@openzeppelin/community-contracts/account/Account.sol"; import {Initializable} from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; import {SignerECDSA} from "@openzeppelin/contracts/utils/cryptography/signers/SignerECDSA.sol"; contract MyAccount is Initializable, Account, SignerECDSA, ... { // ... function initializeECDSA(address signer) public initializer { _setSigner(signer); } } ``` -------------------------------- ### Transferring Tokens Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/docs/modules/ROOT/pages/erc6909.adoc This example demonstrates transferring tokens from one address to another. It shows transferring token ID 2 to a player address and then verifying the balances. ```javascript > gameItems.transfer(playerAddress, 2, 1) > gameItems.balanceOf(playerAddress, 2) 1 > gameItems.balanceOf(deployerAddress, 2) 0 ``` -------------------------------- ### MyGovernor: Governor Contract Setup Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/docs/modules/ROOT/pages/governance.adoc Configures a Governor contract with voting delay, period, and proposal threshold using OpenZeppelin modules. ```solidity pragma solidity ^8.20.0; import "@openzeppelin/contracts/governance/Governor.sol"; import "@openzeppelin/contracts/governance/extensions/GovernorVotes.sol"; import "@openzeppelin/contracts/governance/extensions/GovernorVotesQuorumFraction.sol"; import "@openzeppelin/contracts/governance/extensions/GovernorCountingSimple.sol"; contract MyGovernor is Governor, GovernorVotes, GovernorVotesQuorumFraction, GovernorCountingSimple { constructor(IVotes _token) Governor("MyGovernor") GovernorVotes(_token) GovernorVotesQuorumFraction(400) // 4% quorum GovernorCountingSimple() { // _votingDelay is 1 day in blocks (7200 blocks) _votingDelay = 7200; // _votingPeriod is 1 week in blocks (50400 blocks) _votingPeriod = 50400; } function votingDelay() public view virtual override(Governor, GovernorVotes) returns (uint256) { return super.votingDelay(); } function votingPeriod() public view virtual override(Governor, GovernorVotes) returns (uint256) { return super.votingPeriod(); } function proposalThreshold() public view virtual override(Governor, GovernorVotes) returns (uint256) { return super.proposalThreshold(); } function _quorum(uint256 totalSuppy) internal view virtual override(GovernorVotes, GovernorVotesQuorumFraction) returns (uint256) { return super._quorum(totalSuppy); } function _countVote(uint256 tokenId, bytes32 params, uint256 weight) internal virtual override(Governor, GovernorCountingSimple) { super._countVote(tokenId, params, weight); } } ``` -------------------------------- ### Implement Manual Miner Reward Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/docs/modules/ROOT/pages/erc20-supply.adoc Example of minting tokens to the block proposer using the _mint function triggered by a public function call. ```solidity contract ERC20WithMinerReward is ERC20 { constructor() ERC20("Reward", "RWD") {} function mintMinerReward() public { _mint(block.coinbase, 1000); } } ``` -------------------------------- ### ERC1155 Holder Contract Example Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/docs/modules/ROOT/pages/erc1155.adoc A Solidity example demonstrating how to inherit from ERC1155Holder to allow a contract to receive ERC1155 tokens. Remember to implement functionality for transferring tokens out of the contract. ```solidity pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; import "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol"; contract MyERC115HolderContract is ERC1155, ERC1155Holder { constructor() ERC1155("uri") {} function onReceived(address operator, address from, uint256 id, uint256 value, bytes calldata data) public view override returns (bytes4) { return "0x"; // TODO: implement } function onReceivedBatch(address operator, address from, uint256[] calldata ids, uint256[] calldata values, bytes calldata data) public view override returns (bytes4) { return "0x"; // TODO: implement } } ``` -------------------------------- ### Setup Weighted Multisig Account Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/docs/modules/ROOT/pages/multisig.adoc Illustrates initializing a weighted multisig account, assigning custom weights to signers, and setting a threshold. The sum of weights must meet or exceed the threshold for operations to be valid. ```solidity // Example setup for weighted multisig function setupWeightedMultisigAccount() external { // Create array of signers (same as above) bytes[] memory signers = new bytes[](3); signers[0] = ecdsaSigner; signers[1] = p256Signer; signers[2] = rsaSigner; // Assign weights to signers (Alice:1, Bob:2, Charlie:3) uint256[] memory weights = new uint256[](3); weights[0] = 1; weights[1] = 2; weights[2] = 3; // Set threshold to 4 (requires at least Bob+Charlie or all three) uint256 threshold = 4; // Initialize the weighted account myWeightedMultisigAccount.initialize(signers, weights, threshold); } ``` -------------------------------- ### Prefix interface names with I Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/GUIDELINES.md Interface definitions must start with a capital I. ```solidity interface IERC777 { ``` -------------------------------- ### Construct an ERC-20 Token Contract Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/docs/modules/ROOT/pages/erc20.adoc Example of a custom ERC-20 token contract inheriting from OpenZeppelin's ERC20 implementation. ```solidity include::api:example$token/ERC20/GLDToken.sol[] ``` -------------------------------- ### Construct and Send a UserOp for Delegated EOA Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/docs/modules/ROOT/pages/eoa-delegation.adoc Construct a UserOp object with necessary fields like sender, nonce, callData, gas limits, and fees. Sign the UserOp hash with the EOA and submit it to the entry point contract's handleOps function. This example requires the entrypoint, eoa, eoaClient, publicClient, and EntrypointV08Abi to be defined. ```typescript const userOp = { sender: eoa.address, nonce: await entrypoint.read.getNonce([eoa.address, 0n]), initCode: "0x" as Hex, callData: '0x', accountGasLimits: encodePacked( ["uint128", "uint128"], [ 100_000n, // verificationGas 300_000n, // callGas ] ), preVerificationGas: 50_000n, gasFees: encodePacked( ["uint128", "uint128"], [ 0n, // maxPriorityFeePerGas 0n, // maxFeePerGas ] ), paymasterAndData: "0x" as Hex, signature: "0x" as Hex, }; const userOpHash = await entrypoint.read.getUserOpHash([userOp]); userOp.signature = await eoa.sign({ hash: userOpHash }); const userOpReceipt = await eoaClient .writeContract({ abi: EntrypointV08Abi, address: entrypoint.address, authorizationList: [authorization], functionName: "handleOps", args: [[userOp], eoa.address], }) .then((txHash) => publicClient.waitForTransactionReceipt({ hash: txHash, }) ); console.log(userOpReceipt); ``` -------------------------------- ### PaymasterUSDCChainlink Contract Example Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/docs/modules/ROOT/pages/paymasters.adoc An example of a paymaster contract that uses Chainlink price feeds to determine the exchange rate between USDC and ETH. Note that the price feed addresses are specific to the Sepolia network and must be updated for other networks or production use. ```solidity // WARNING: Unaudited code. // Consider performing a security review before going to production. contract PaymasterUSDCChainlink is PaymasterERC20, Ownable { // Values for sepolia // See https://docs.chain.link/data-feeds/price-feeds/addresses AggregatorV3Interface public constant USDC_USD_ORACLE = AggregatorV3Interface(0xA2F78ab2355fe2f984D808B5CeE7FD0A93D5270E); AggregatorV3Interface public constant ETH_USD_ORACLE = AggregatorV3Interface(0x694AA1769357215DE4FAC081bf1f309aDC325306); // See https://sepolia.etherscan.io/token/0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238 IERC20 private constant USDC = IERC20(0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238); constructor(address initialOwner) Ownable(initialOwner) {} function _authorizeWithdraw() internal virtual override onlyOwner {} function liveness() public view virtual returns (uint256) { return 15 minutes; // Tolerate stale data } /// @dev Floor: refuse to sponsor when 1 ETH is worth less than 100 USDC. /// 1e8 = 100 (USDC per ETH) * 10**6 (USDC decimals); the 1e18 denom cancels 1e18 wei/ETH. function _minTokensPerNative() internal view virtual override returns (uint256) { return 1e8; } function _fetchDetails( PackedUserOperation calldata userOp, bytes32 /* userOpHash */ ) internal view virtual override returns (uint256 validationData, IERC20 token, uint256 tokenPerNative) { (uint256 validationData_, uint256 price) = _fetchOracleDetails(userOp); return ( validationData_, USDC, price ); } function _fetchOracleDetails( PackedUserOperation calldata /* userOp */ ) internal view virtual returns (uint256 validationData, uint256 tokenPerNative) { // ... } } ``` -------------------------------- ### Configure Foundry Remappings Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/docs/modules/ROOT/pages/index.adoc Adds the necessary remapping for OpenZeppelin Contracts when installed via git with Foundry. This ensures correct contract resolution. ```bash Add @openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/ in remappings.txt. ``` -------------------------------- ### ERC6909GameItems Contract Example Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/docs/modules/ROOT/pages/erc6909.adoc This contract demonstrates how to implement ERC-6909 for tokenized game items, including minting all items in the constructor and using the ERC6909Metadata extension for decimals. ```solidity pragma solidity ^0.8.20; import "@openzeppelin/contracts/token/ERC6909/ERC6909.sol"; import "@openzeppelin/contracts/token/ERC6909/extensions/ERC6909Metadata.sol"; import "@openzeppelin/contracts/token/ERC6909/extensions/ERC6909TokenSupply.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; contract ERC6909GameItems is ERC6909, ERC6909Metadata, ERC6909TokenSupply, Ownable { constructor() { // Mint all items to the deployer _mint(msg.sender, 0, 1000000000); _mint(msg.sender, 1, 1000000000); _mint(msg.sender, 2, 1000000000); _mint(msg.sender, 3, 1000000000); // Set metadata for each item type _setMetadata(0, "Sword", "SWD", 0); _setMetadata(1, "Shield", "SHD", 0); _setMetadata(2, "Potion", "POT", 0); _setMetadata(3, "Gold", "GLD", 18); } // The following functions are overrides required by Solidity 0.8.20+ function _update(address to, uint256[] memory ids, int256[] memory amounts) internal override(ERC6909, ERC6909TokenSupply) returns (uint256[] memory) { return super._update(to, ids, amounts); } function supportsInterface(bytes4 interfaceId) public view override(ERC6909, ERC6909Metadata, ERC6909TokenSupply) returns (bool) { return super.supportsInterface(interfaceId); } } ``` -------------------------------- ### Enable ERC-7821 Batched Execution Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/docs/modules/ROOT/pages/accounts.adoc Extends an account to support batched execution using ERC-7821. This example overrides the authorization check to allow the entrypoint to execute batches. ```Solidity import {Account} from "@openzeppelin/community-contracts/account/Account.sol"; import {ERC7821} from "@openzeppelin/community-contracts/account/extensions/draft-ERC7821.sol"; import {SignerEIP7702} from "@openzeppelin/contracts/utils/cryptography/signers/SignerEIP7702.sol"; contract MyAccount is Account, SignerEIP7702, ERC7821 { // Override to allow the entrypoint to execute batches function _erc7821AuthorizedExecutor( address caller, bytes32 mode, bytes calldata executionData ) internal view virtual override returns (bool) { return caller == address(entryPoint()) || super._erc7821AuthorizedExecutor(caller, mode, executionData); } } ``` -------------------------------- ### Send UserOperation via handleOps Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/docs/modules/ROOT/pages/accounts.adoc Send a UserOperation by calling the `handleOps` function on the Entrypoint contract. This example demonstrates how to write the contract call and wait for the transaction receipt. Ensure you have the correct Entrypoint ABI and address. ```typescript // Send the UserOperation const userOpReceipt = await walletClient .writeContract({ abi: [/* ENTRYPOINT ABI */], address: '0x', functionName: "handleOps", args: [[userOp], eoa.address], }) .then((txHash) => publicClient.waitForTransactionReceipt({ hash: txHash, }) ); // Print receipt console.log(userOpReceipt); ``` -------------------------------- ### Dynamic Role Management with Default Admin Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/docs/modules/ROOT/pages/access-control.adoc Example of an ERC-20 token contract utilizing the default admin role for dynamic permission management. ```solidity include::api:example$access-control/AccessControlERC20MintMissing.sol[] ``` -------------------------------- ### Memory Management for Loops Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/docs/modules/ROOT/pages/utilities.adoc The Memory library aids in granular memory management, useful for avoiding memory expansion costs in loops. This example shows processing multiple items with temporary data allocation. ```solidity function processMultipleItems(uint256[] memory items) internal { for (uint256 i = 0; i < items.length; i++) { bytes memory tempData = abi.encode(items[i], block.timestamp); // Process tempData... } } ``` -------------------------------- ### ERC4626 Proportional Fee Implementation Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/docs/modules/ROOT/pages/erc4626.adoc This example demonstrates how to implement fees that are proportional to the deposited or withdrawn amount in an ERC4626 compliant vault. It shows the necessary adjustments to handle fee calculations during deposit and withdrawal operations. ```solidity import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ ERC4626/ERC4626.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; contract ERC4626Fees is ERC4626, Ownable { uint256 public feeNumerator; uint256 public feeDenominator; constructor(IERC20 _asset, string memory _name, string memory _symbol) ERC4626(_asset, _name, _symbol) {} function setFee(uint256 _feeNumerator, uint256 _feeDenominator) external onlyOwner { feeNumerator = _feeNumerator; feeDenominator = _feeDenominator; } function _mintShares(address to, uint256 shares) internal virtual override { uint256 feeShares = (shares * feeNumerator) / feeDenominator; super._mintShares(to, shares - feeShares); // Optionally, you could mint the feeShares to the owner or a treasury // super._mintShares(owner(), feeShares); } function _mint(address to, uint256 amount) internal virtual override { uint256 feeAmount = (amount * feeNumerator) / feeDenominator; super._mint(to, amount - feeAmount); // Optionally, you could mint the feeAmount to the owner or a treasury // super._mint(owner(), feeAmount); } function _withdraw(address to, uint256 amount, uint256 যেন) internal virtual override { uint256 feeAmount = (amount * feeNumerator) / feeDenominator; super._withdraw(to, amount - feeAmount, যেন); // Optionally, you could withdraw the feeAmount to the owner or a treasury // super._withdraw(owner(), feeAmount, ...); } function _burnShares(address from, uint256 shares) internal virtual override { uint256 feeShares = (shares * feeNumerator) / feeDenominator; super._burnShares(from, shares - feeShares); // Optionally, you could burn the feeShares from the owner or a treasury // super._burnShares(owner(), feeShares); } } ``` -------------------------------- ### Querying Token Balance Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/docs/modules/ROOT/pages/erc6909.adoc Use this method to query the balance of a specific token ID for a given address. The example shows querying the balance of token ID 3 for the deployer's address. ```javascript > gameItems.balanceOf(deployerAddress, 3) 1000000000 ``` -------------------------------- ### Encode ERC-7821 Batch Data with Viem Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/docs/modules/ROOT/pages/accounts.adoc Example using Viem utilities to encode data for an ERC-7821 batch execution. This demonstrates encoding the mode and batch entries. ```TypeScript // CALL_TYPE_BATCH, EXEC_TYPE_DEFAULT, ..., selector, payload const mode = encodePacked( ["bytes1", "bytes1", "bytes4", "bytes4", "bytes22"], ["0x01", "0x00", "0x00000000", "0x00000000", "0x00000000000000000000000000000000000000000000"] ); const entries = [ { target: "0x000...0001", value: 0n, data: "0x000...000", }, { target: "0x000...0002", value: 0n, data: "0x000...000", } ]; const batch = encodeAbiParameters( [parseAbiParameter("(address,uint256,bytes)[]")], [ entries.map<[Address, bigint, Hex]>((entry) => ``` -------------------------------- ### Get UserOperation Hash and Sign Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/docs/modules/ROOT/pages/accounts.adoc Obtain the UserOperation hash using the Entrypoint's `getUserOpHash` function and then sign the raw hash. Note that this may result in a poorer user experience as users see an opaque hash. ```typescript const userOpHash = await entrypoint.read.getUserOpHash([userOp]); userOp.signature = await eoa.sign({ hash: userOpHash }); ``` -------------------------------- ### List make script help Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/fv/README.md Display available make commands for the formal verification directory. ```bash make -C fv help ``` -------------------------------- ### ERC1155 Metadata JSON Schema Example Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/docs/modules/ROOT/pages/erc1155.adoc An example of the JSON document structure for ERC1155 token metadata. This data is typically served from a URI and is not stored on-chain. ```json { "name": "Thor's hammer", "description": "Mjölnir, the legendary hammer of the Norse god of thunder.", "image": "https://game.example/item-id-8u5h2m.png", "strength": 20 } ``` -------------------------------- ### Example EIP-7702 Delegate Contract Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/docs/modules/ROOT/pages/eoa-delegation.adoc This is an example of a smart contract that can act as a delegate for an EOA using EIP-7702. Ensure your delegate contracts require signatures that avoid replayability. ```solidity include::api:example$account/MyAccountEIP7702.sol[] ``` -------------------------------- ### Initialize a Merkle Tree Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/docs/modules/ROOT/pages/utilities.adoc Set up a Merkle Tree structure using the MerkleTree library. ```solidity using MerkleTree for MerkleTree.Bytes32PushTree; MerkleTree.Bytes32PushTree private _tree; function setup(uint8 _depth, bytes32 _zero) public /* onlyOwner */ { root = _tree.setup(_depth, _zero); } function push(bytes32 leaf) public /* onlyOwner */ { ``` -------------------------------- ### Implement Fixed Supply (Modern Pattern) Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/docs/modules/ROOT/pages/erc20-supply.adoc Recommended approach using the internal _mint function to safely initialize token supply and emit required events. ```solidity contract ERC20FixedSupply is ERC20 { constructor() ERC20("Fixed", "FIX") { _mint(msg.sender, 1000); } } ``` -------------------------------- ### Deploy Upgradeable Proxy with Hardhat Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/docs/modules/ROOT/pages/upgradeable.adoc Use the OpenZeppelin Upgrades Plugins with Hardhat to deploy an upgradeable proxy contract. ```javascript // scripts/deploy-my-collectible.js const { ethers, upgrades } = require("hardhat"); async function main() { const MyCollectible = await ethers.getContractFactory("MyCollectible"); const mc = await upgrades.deployProxy(MyCollectible); await mc.waitForDeployment(); console.log("MyCollectible deployed to:", await mc.getAddress()); } main(); ``` -------------------------------- ### Prepare a UserOperation with viem Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/docs/modules/ROOT/pages/accounts.adoc Constructs a UserOperation struct with necessary fields for execution by the EntryPoint. Ensure your account is deployed or provide initCode. ```typescript import { getContract, createWalletClient, http, Hex } from 'viem'; const walletClient = createWalletClient({ account, chain, transport: http(), }) const entrypoint = getContract({ abi: [/* ENTRYPOINT ABI */], address: '0x', client: walletClient, }); const userOp = { sender: '0x', nonce: await entrypoint.read.getNonce([sender, 0n]), initCode: "0x" as Hex, callData: '0x', accountGasLimits: encodePacked( ["uint128", "uint128"], [ 100_000n, // verificationGasLimit 300_000n, // callGasLimit ] ), preVerificationGas: 50_000n, gasFees: encodePacked( ["uint128", "uint128"], [ 0n, // maxPriorityFeePerGas 0n, // maxFeePerGas ] ), paymasterAndData: "0x" as Hex, signature: "0x" as Hex, }; ``` -------------------------------- ### Calculate Token Transfer Amount Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/docs/modules/ROOT/pages/erc20.adoc Example of calculating the integer value for a transfer based on 18 decimal places. ```solidity transfer(recipient, 5 * (10 ** 18)); ``` -------------------------------- ### Implement Fixed Supply (Legacy Pattern) Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/docs/modules/ROOT/pages/erc20-supply.adoc Legacy approach to setting initial supply by directly modifying internal state variables, which is now discouraged. ```solidity contract ERC20FixedSupply is ERC20 { constructor() { totalSupply += 1000; balances[msg.sender] += 1000; } } ``` -------------------------------- ### Execute verification script Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/fv/README.md Run the verification script from the repository root to submit jobs to the Certora Verification service. ```bash node fv/run.js [SPEC_NAME | fv/specs/NAME.conf] [--all] [-p N] [-v] ``` -------------------------------- ### Standard Multi-Signature Account Implementation Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/docs/modules/ROOT/pages/multisig.adoc Implements a standard multi-signature account using MultiSignerERC7913. This is suitable for setups where each signer has equal authority and a fixed number of approvals are required. ```solidity import {MultiSignerERC7913} from "@openzeppelin/contracts/utils/cryptography/signers/MultiSignerERC7913.sol"; contract MyAccountMultiSigner is Account, EIP712, MultiSignerERC7913, ERC7739, ERC7821, ERC721Holder, ERC1155Holder, Initializable { constructor() EIP712("MyAccountMultiSigner", "1") {} function initialize(bytes[] memory signers, uint256 threshold) public initializer { _addSigners(signers); _setThreshold(threshold); } function addSigners(bytes[] memory signers) public onlyEntryPointOrSelf { _addSigners(signers); } function removeSigners(bytes[] memory signers) public onlyEntryPointOrSelf { _removeSigners(signers); } function setThreshold(uint256 threshold) public onlyEntryPointOrSelf { _setThreshold(threshold); } /// @dev Allows the entry point as an authorized executor. function _erc7821AuthorizedExecutor( address caller, bytes32 mode, bytes calldata executionData ) internal view virtual override returns (bool) { return caller == address(entryPoint()) || super._erc7821AuthorizedExecutor(caller, mode, executionData); } } ``` -------------------------------- ### Multicall for Bundled Transactions Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/docs/modules/ROOT/pages/utilities.adoc The Multicall contract bundles multiple calls into a single external call, enabling atomic operations and transaction reversion. This example shows how to use it with Ethers.js. ```solidity include::api:example$utilities/Multicall.sol[] ``` -------------------------------- ### Override revokeRole to prevent admin revocation Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/docs/modules/ROOT/pages/extending-contracts.adoc This example demonstrates overriding the `revokeRole` function in `AccessControl` to prevent the `DEFAULT_ADMIN_ROLE` from being revoked. It uses `super.revokeRole` to call the original function for other roles. ```solidity // contracts/AccessControlNonRevokableAdmin.sol // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import {AccessControl} from "../../../access/AccessControl.sol"; contract AccessControlNonRevokableAdmin is AccessControl { error AccessControlNonRevokable(); function revokeRole(bytes32 role, address account) public override { if (role == DEFAULT_ADMIN_ROLE) { revert AccessControlNonRevokable(); } super.revokeRole(role, account); } } ``` -------------------------------- ### ERC-1155 Game Items Contract Example Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/docs/modules/ROOT/pages/erc1155.adoc A Solidity contract implementing ERC-1155 to manage multiple game items, including both fungible and non-fungible tokens. Items are minted to the deployer in the constructor. ```solidity pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; /** * @title GameItems * @dev Uses ERC1155 to track multiple items in a game. * All items are minted to the deployer of the contract. */ contract GameItems is ERC1155, Ownable { constructor() ERC1155("https://game.example/api/item/{id}.json") {} function uri(uint256 _id) override public view returns (string memory) { return super.uri(_id); } // Example function to mint items to the deployer function mint(uint256 _id, uint256 _amount) external onlyOwner { _mint(owner(), _id, _amount, ""); } // Example function to mint multiple items to the deployer function mintBatch(uint256[] memory _ids, uint256[] memory _amounts) external onlyOwner { _mintBatch(owner(), _ids, _amounts, ""); } } ``` -------------------------------- ### Queue Proposal Actions with Timelock Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/docs/modules/ROOT/pages/governance.adoc Queues a proposal's actions for execution via the timelock contract. Requires the proposal parameters and a hash of the description. ```javascript const descriptionHash = ethers.utils.id(“Proposal #1: Give grant to team”); await governor.queue( [tokenAddress], [0], [transferCalldata], descriptionHash, ); ``` -------------------------------- ### ERC-721 Token Metadata JSON Schema Source: https://github.com/openzeppelin/openzeppelin-contracts/blob/master/docs/modules/ROOT/pages/erc721.adoc An example JSON structure for token metadata, including name, description, image URI, and custom attributes. This schema is typically hosted at the URI returned by `tokenURI`. ```json { "name": "Thor's hammer", "description": "Mjölnir, the legendary hammer of the Norse god of thunder.", "image": "https://game.example/item-id-8u5h2m.png", "strength": 20 } ```