Contract Name:
BytecodeRepository
Contract Source Code:
<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: BUSL-1.1
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2025.
pragma solidity ^0.8.23;
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {Create2} from "@openzeppelin/contracts/utils/Create2.sol";
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {LibString} from "@solady/utils/LibString.sol";
import {SSTORE2} from "@solady/utils/SSTORE2.sol";
import {IVersion} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IVersion.sol";
import {SanityCheckTrait} from "@gearbox-protocol/core-v3/contracts/traits/SanityCheckTrait.sol";
import {EIP712Mainnet} from "../helpers/EIP712Mainnet.sol";
import {IBytecodeRepository} from "../interfaces/IBytecodeRepository.sol";
import {AuditReport, Bytecode, BytecodePointer} from "../interfaces/Types.sol";
import {AP_BYTECODE_REPOSITORY} from "../libraries/ContractLiterals.sol";
import {Domain} from "../libraries/Domain.sol";
import {ImmutableOwnableTrait} from "../traits/ImmutableOwnableTrait.sol";
/// @title Bytecode repository
contract BytecodeRepository is ImmutableOwnableTrait, SanityCheckTrait, IBytecodeRepository, EIP712Mainnet {
using EnumerableSet for EnumerableSet.AddressSet;
using EnumerableSet for EnumerableSet.Bytes32Set;
using EnumerableSet for EnumerableSet.UintSet;
using LibString for bytes32;
using LibString for string;
using LibString for uint256;
using Domain for bytes32;
/// @dev Internal struct with version info for a given contract type
struct VersionInfo {
address owner;
uint256 latest;
mapping(uint256 majorVersion => uint256) latestByMajor;
mapping(uint256 minorVersion => uint256) latestByMinor;
EnumerableSet.UintSet versionsSet;
}
/// @notice Contract version
uint256 public constant override version = 3_10;
/// @notice Contract type
bytes32 public constant override contractType = AP_BYTECODE_REPOSITORY;
/// @notice Bytecode typehash
bytes32 public constant override BYTECODE_TYPEHASH =
keccak256("Bytecode(bytes32 contractType,uint256 version,bytes initCode,address author,string source)");
/// @notice Audit report typehash
bytes32 public constant override AUDIT_REPORT_TYPEHASH =
keccak256("AuditReport(bytes32 bytecodeHash,address auditor,string reportUrl)");
/// @dev Mapping from `deployedContract` deployed from the repository to its bytecode hash
mapping(address deployedContract => bytes32) _deployedContractBytecodeHashes;
/// @dev Mapping from `bytecodeHash` to pointer to bytecode with given hash
mapping(bytes32 bytecodeHash => BytecodePointer) internal _bytecodeByHash;
/// @dev Mapping from `bytecodeHash` to its audit reports
mapping(bytes32 bytecodeHash => AuditReport[]) internal _auditReports;
/// @dev Mapping from `cType` to `ver` to allowed bytecode hash
mapping(bytes32 cType => mapping(uint256 ver => bytes32 bytecodeHash)) internal _allowedBytecodeHashes;
/// @dev Set of system domains
EnumerableSet.Bytes32Set internal _systemDomainsSet;
/// @dev Set of public domains
EnumerableSet.Bytes32Set internal _publicDomainsSet;
/// @dev Set of approved auditors
EnumerableSet.AddressSet internal _auditorsSet;
/// @dev Mapping from `auditor` to their name
mapping(address auditor => string) internal _auditorNames;
/// @dev Mapping from `initCodeHash` to whether it is forbidden
mapping(bytes32 initCodeHash => bool) internal _isInitCodeForbidden;
/// @dev Mapping from `token` to its specific postfix
mapping(address token => bytes32) internal _tokenSpecificPostfixes;
/// @dev Mapping from `cType` to its version info
mapping(bytes32 cType => VersionInfo) internal _versionInfo;
/// @notice Constructor
/// @param owner_ Owner of the bytecode repository
constructor(address owner_)
EIP712Mainnet(contractType.fromSmallString(), version.toString())
ImmutableOwnableTrait(owner_)
{}
// --------------- //
// EIP-712 GETTERS //
// --------------- //
/// @notice Returns the domain separator
function domainSeparatorV4() external view override returns (bytes32) {
return _domainSeparatorV4();
}
/// @notice Computes bytecode's struct hash
/// @dev `authorSignature` field is ignored
function computeBytecodeHash(Bytecode calldata bytecode) public pure override returns (bytes32) {
return keccak256(
abi.encode(
BYTECODE_TYPEHASH,
bytecode.contractType,
bytecode.version,
keccak256(bytecode.initCode),
bytecode.author,
keccak256(bytes(bytecode.source))
)
);
}
/// @notice Computes audit report's struct hash
/// @dev `signature` field is ignored
function computeAuditReportHash(bytes32 bytecodeHash, AuditReport calldata report)
public
pure
override
returns (bytes32)
{
return keccak256(
abi.encode(AUDIT_REPORT_TYPEHASH, bytecodeHash, report.auditor, keccak256(bytes(report.reportUrl)))
);
}
// ------------------- //
// DEPLOYING CONTRACTS //
// ------------------- //
/// @notice Whether `deployedContract` was deployed from the repository
function isDeployedFromRepository(address deployedContract) external view override returns (bool) {
return _deployedContractBytecodeHashes[deployedContract] != bytes32(0);
}
/// @notice Returns bytecode hash for `deployedContract` deployed from the repository
function getDeployedContractBytecodeHash(address deployedContract) external view override returns (bytes32) {
return _deployedContractBytecodeHashes[deployedContract];
}
/// @notice Computes the address at which a contract of a given type and version
/// with given constructor parameters and salt would be deployed
/// @dev Deployer's address is mixed with salt to prevent front-running using collisions
function computeAddress(bytes32 cType, uint256 ver, bytes memory constructorParams, bytes32 salt, address deployer)
external
view
override
returns (address)
{
bytes32 bytecodeHash = _allowedBytecodeHashes[cType][ver];
BytecodePointer storage bytecode = _bytecodeByHash[bytecodeHash];
bytes memory initCode = _readInitCode(bytecode.initCodePointers);
bytes32 uniqueSalt = keccak256(abi.encode(salt, deployer));
bytes memory bytecodeWithParams = abi.encodePacked(initCode, constructorParams);
return Create2.computeAddress(uniqueSalt, keccak256(bytecodeWithParams));
}
/// @notice Deploys a contract of a given type and version with given constructor parameters and salt.
/// Tries to transfer ownership over the deployed contract to the caller.
/// Bytecode must be allowed either as system or public contract, which, in turn, requires it
/// to be uploaded and have at least one signed report from approved auditor.
/// @dev Deployer's address is mixed with salt to prevent front-running using collisions
/// @dev Reverts if contract's init code is forbidden
/// @dev Reverts if contract was previously deployed at the same address
/// @dev Reverts if deployed contract's type or version does not match passed parameters
function deploy(bytes32 cType, uint256 ver, bytes memory constructorParams, bytes32 salt)
external
override
returns (address newContract)
{
bytes32 bytecodeHash = _allowedBytecodeHashes[cType][ver];
if (bytecodeHash == 0) revert BytecodeIsNotAllowedException(cType, ver);
BytecodePointer storage bytecode = _bytecodeByHash[bytecodeHash];
bytes memory initCode = _readInitCode(bytecode.initCodePointers);
_revertIfInitCodeIsForbidden(initCode);
bytes32 uniqueSalt = keccak256(abi.encode(salt, msg.sender));
bytes memory bytecodeWithParams = abi.encodePacked(initCode, constructorParams);
newContract = Create2.computeAddress(uniqueSalt, keccak256(bytecodeWithParams));
if (newContract.code.length != 0) revert ContractIsAlreadyDeployedException(newContract);
Create2.deploy(0, uniqueSalt, bytecodeWithParams);
if (IVersion(newContract).contractType() != cType || IVersion(newContract).version() != ver) {
revert InvalidBytecodeException(bytecodeHash);
}
_deployedContractBytecodeHashes[newContract] = bytecodeHash;
emit DeployContract(bytecodeHash, cType, ver, newContract, constructorParams);
try Ownable(newContract).transferOwnership(msg.sender) {} catch {}
}
// ------------------ //
// UPLOADING BYTECODE //
// ------------------ //
/// @notice Returns bytecode with `bytecodeHash`
/// @dev Reverts if bytecode is not uploaded
function getBytecode(bytes32 bytecodeHash) external view override returns (Bytecode memory) {
BytecodePointer memory bytecode = _bytecodeByHash[bytecodeHash];
if (bytecode.initCodePointers.length == 0) revert BytecodeIsNotUploadedException(bytecodeHash);
return Bytecode({
contractType: bytecode.contractType,
version: bytecode.version,
initCode: _readInitCode(bytecode.initCodePointers),
author: bytecode.author,
source: bytecode.source,
authorSignature: bytecode.authorSignature
});
}
/// @notice Whether bytecode with `bytecodeHash` is uploaded
function isBytecodeUploaded(bytes32 bytecodeHash) public view override returns (bool) {
return _bytecodeByHash[bytecodeHash].initCodePointers.length != 0;
}
/// @notice Uploads new contract bytecode to the repository.
/// Simply uploading the bytecode is not enough to deploy a contract with it, see `deploy` for details.
/// @dev Reverts if bytecode's contract type is invalid or version is less than `100` or greater than `999`
/// @dev Reverts if bytecode for given contract type and version is already allowed
/// @dev Reverts if author is zero address or if their signature is invalid
/// @dev Reverts if init code is empty
/// @dev On mainnet, only author of the bytecode can upload it
function uploadBytecode(Bytecode calldata bytecode) external override nonZeroAddress(bytecode.author) {
bytes32 bytecodeHash = computeBytecodeHash(bytecode);
if (isBytecodeUploaded(bytecodeHash)) return;
_validateContractType(bytecode.contractType);
_validateVersion(bytecode.contractType, bytecode.version);
if (_allowedBytecodeHashes[bytecode.contractType][bytecode.version] != 0) {
revert BytecodeIsAlreadyAllowedException(bytecode.contractType, bytecode.version);
}
if (block.chainid == 1 && msg.sender != bytecode.author) revert CallerIsNotBytecodeAuthorException(msg.sender);
address author = ECDSA.recover(_hashTypedDataV4(bytecodeHash), bytecode.authorSignature);
if (author != bytecode.author) revert InvalidAuthorSignatureException(author);
address[] memory initCodePointers = _writeInitCode(bytecode.initCode);
_bytecodeByHash[bytecodeHash] = BytecodePointer({
contractType: bytecode.contractType,
version: bytecode.version,
initCodePointers: initCodePointers,
author: bytecode.author,
source: bytecode.source,
authorSignature: bytecode.authorSignature
});
emit UploadBytecode(
bytecodeHash,
bytecode.contractType,
bytecode.version,
bytecode.author,
bytecode.source,
bytecode.authorSignature
);
}
/// @dev Stores contract's init code using `SSTORE2`, splitting it into chunks if needed
function _writeInitCode(bytes calldata initCode) internal returns (address[] memory initCodePointers) {
uint256 chunkSize = 24500; // small buffer to account for `SSTORE2` overhead
if (initCode.length == 0) revert InitCodeIsEmptyException();
uint256 len = (initCode.length - 1) / chunkSize + 1;
initCodePointers = new address[](len);
for (uint256 i; i < len; ++i) {
uint256 start = i * chunkSize;
uint256 end = start + chunkSize;
if (end > initCode.length) end = initCode.length;
initCodePointers[i] = SSTORE2.write(initCode[start:end]);
}
}
/// @dev Reads stored contract's init code using `SSTORE2`
function _readInitCode(address[] memory initCodePointers) internal view returns (bytes memory initCode) {
for (uint256 i; i < initCodePointers.length; ++i) {
initCode = bytes.concat(initCode, SSTORE2.read(initCodePointers[i]));
}
}
// ----------------- //
// AUDITING BYTECODE //
// ----------------- //
/// @notice Whether bytecode with `bytecodeHash` is signed at least by one approved auditor
function isBytecodeAudited(bytes32 bytecodeHash) public view override returns (bool) {
uint256 len = _auditReports[bytecodeHash].length;
for (uint256 i; i < len; ++i) {
AuditReport memory report = _auditReports[bytecodeHash][i];
if (isAuditor(report.auditor)) return true;
}
return false;
}
/// @notice Returns all audit reports for `bytecodeHash`
function getAuditReports(bytes32 bytecodeHash) external view override returns (AuditReport[] memory) {
return _auditReports[bytecodeHash];
}
/// @notice Returns audit report at `index` for `bytecodeHash`
function getAuditReport(bytes32 bytecodeHash, uint256 index) external view override returns (AuditReport memory) {
return _auditReports[bytecodeHash][index];
}
/// @notice Returns number of audit reports for `bytecodeHash`
function getNumAuditReports(bytes32 bytecodeHash) external view override returns (uint256) {
return _auditReports[bytecodeHash].length;
}
/// @notice Submits signed audit report for bytecode with `bytecodeHash`
/// @dev Reverts if bytecode is not uploaded
/// @dev Reverts if auditor is not approved, already signed bytecode, or their signature is invalid
function submitAuditReport(bytes32 bytecodeHash, AuditReport calldata auditReport) external override {
if (!isBytecodeUploaded(bytecodeHash)) revert BytecodeIsNotUploadedException(bytecodeHash);
if (!_auditorsSet.contains(auditReport.auditor)) revert AuditorIsNotApprovedException(auditReport.auditor);
bytes32 reportHash = computeAuditReportHash(bytecodeHash, auditReport);
address auditor = ECDSA.recover(_hashTypedDataV4(reportHash), auditReport.signature);
if (auditor != auditReport.auditor) revert InvalidAuditorSignatureException(auditor);
AuditReport[] storage reports = _auditReports[bytecodeHash];
uint256 len = reports.length;
for (uint256 i; i < len; ++i) {
if (keccak256(reports[i].signature) == keccak256(auditReport.signature)) {
revert BytecodeIsAlreadySignedByAuditorException(bytecodeHash, auditor);
}
}
reports.push(auditReport);
emit AuditBytecode(bytecodeHash, auditor, auditReport.reportUrl, auditReport.signature);
}
// ----------------- //
// ALLOWING BYTECODE //
// ----------------- //
/// @notice Returns the allowed bytecode hash for `cType` and `ver`
function getAllowedBytecodeHash(bytes32 cType, uint256 ver) external view override returns (bytes32) {
return _allowedBytecodeHashes[cType][ver];
}
/// @notice Returns the owner of `cType`
function getContractTypeOwner(bytes32 cType) external view override returns (address) {
return _versionInfo[cType].owner;
}
/// @notice Marks bytecode with `bytecodeHash` as allowed system contract.
/// Adds bytecode's domain to the list of system domains.
/// @dev Can only be called by the owner
/// @dev Reverts if bytecode is not uploaded or not audited
/// @dev Reverts if bytecode's contract type is in the list of public domains
/// @dev Reverts if bytecode with this contract type and version is already allowed
function allowSystemContract(bytes32 bytecodeHash) external override onlyOwner {
if (!isBytecodeUploaded(bytecodeHash)) revert BytecodeIsNotUploadedException(bytecodeHash);
if (!isBytecodeAudited(bytecodeHash)) revert BytecodeIsNotAuditedException(bytecodeHash);
BytecodePointer storage bytecode = _bytecodeByHash[bytecodeHash];
bytes32 cType = bytecode.contractType;
_addSystemDomain(cType.extractDomain());
_allowContract(bytecodeHash, cType, bytecode.version);
}
/// @notice Marks bytecode with `bytecodeHash` as allowed public contract.
/// Sets bytecode's author as contract type owner.
/// @dev Reverts if bytecode is not uploaded or not audited
/// @dev Reverts if bytecode's contract type is not in the list of public domains
/// @dev Reverts if bytecode's author is not contract type owner
/// @dev Reverts if bytecode with this contract type and version is already allowed
function allowPublicContract(bytes32 bytecodeHash) external override {
if (!isBytecodeUploaded(bytecodeHash)) revert BytecodeIsNotUploadedException(bytecodeHash);
if (!isBytecodeAudited(bytecodeHash)) revert BytecodeIsNotAuditedException(bytecodeHash);
BytecodePointer storage bytecode = _bytecodeByHash[bytecodeHash];
bytes32 cType = bytecode.contractType;
if (!isPublicDomain(cType.extractDomain())) revert ContractTypeIsNotInPublicDomainException(cType);
address author = bytecode.author;
address contractTypeOwner = _versionInfo[cType].owner;
if (contractTypeOwner == address(0)) {
_versionInfo[cType].owner = author;
emit SetContractTypeOwner(cType, author);
} else if (contractTypeOwner != author) {
revert AuthorIsNotContractTypeOwnerException(cType, author);
}
_allowContract(bytecodeHash, cType, bytecode.version);
}
/// @notice Forbids all previously allowed public contracts of a given type, removes type owner and version info.
/// Exists primarily to cleanup the repository after public domain squatting by a compromised auditor.
/// @dev Can only be called by the owner
function removePublicContractType(bytes32 cType) external override onlyOwner {
if (!isPublicDomain(cType.extractDomain())) return;
VersionInfo storage info = _versionInfo[cType];
if (info.owner != address(0)) {
info.owner = address(0);
emit RemoveContractTypeOwner(cType);
}
info.latest = 0;
uint256[] memory versions = info.versionsSet.values();
uint256 numVersions = versions.length;
for (uint256 i; i < numVersions; ++i) {
uint256 ver = versions[i];
info.versionsSet.remove(ver);
info.latestByMajor[_getMajorVersion(ver)] = 0;
info.latestByMinor[_getMinorVersion(ver)] = 0;
bytes32 bytecodeHash = _allowedBytecodeHashes[cType][ver];
_allowedBytecodeHashes[cType][ver] = bytes32(0);
emit ForbidContract(bytecodeHash, cType, ver);
}
}
/// @dev Allows bytecode with `bytecodeHash` for `cType` and `ver`, updates version info for `cType`
/// @dev Reverts if bytecode is already allowed
function _allowContract(bytes32 bytecodeHash, bytes32 cType, uint256 ver) internal {
if (_allowedBytecodeHashes[cType][ver] == bytecodeHash) return;
if (_allowedBytecodeHashes[cType][ver] != 0) revert BytecodeIsAlreadyAllowedException(cType, ver);
_allowedBytecodeHashes[cType][ver] = bytecodeHash;
emit AllowContract(bytecodeHash, cType, ver);
_updateVersionInfo(cType, ver);
}
// ------------------ //
// DOMAINS MANAGEMENT //
// ------------------ //
/// @notice Whether `domain` is in the list of system domains
function isSystemDomain(bytes32 domain) public view override returns (bool) {
return _systemDomainsSet.contains(domain);
}
/// @notice Returns list of all system domains
function getSystemDomains() external view override returns (bytes32[] memory) {
return _systemDomainsSet.values();
}
/// @notice Whether `domain` is in the list of public domains
function isPublicDomain(bytes32 domain) public view override returns (bool) {
return _publicDomainsSet.contains(domain);
}
/// @notice Returns list of all public domains
function getPublicDomains() external view override returns (bytes32[] memory) {
return _publicDomainsSet.values();
}
/// @notice Adds `domain` to the list of public domains
/// @dev Can only be called by the owner
/// @dev Reverts if `domain` is invalid or is already in the list of system domains
function addPublicDomain(bytes32 domain) external override onlyOwner {
_validateDomain(domain);
_addPublicDomain(domain);
}
/// @dev Adds `domain` to the list of public domains
/// @dev Reverts if `domain` is already in the list of system domains
function _addPublicDomain(bytes32 domain) internal {
if (isSystemDomain(domain)) revert DomainIsAlreadyMarkedAsSystemException(domain);
if (_publicDomainsSet.add(domain)) emit AddPublicDomain(domain);
}
/// @dev Adds `domain` to the list of system domains
/// @dev Reverts if `domain` is already in the list of public domains
function _addSystemDomain(bytes32 domain) internal {
if (isPublicDomain(domain)) revert DomainIsAlreadyMarkedAsPublicException(domain);
if (_systemDomainsSet.add(domain)) emit AddSystemDomain(domain);
}
// ------------------- //
// AUDITORS MANAGEMENT //
// ------------------- //
/// @notice Whether `auditor` is an approved auditor
function isAuditor(address auditor) public view override returns (bool) {
return _auditorsSet.contains(auditor);
}
/// @notice Returns list of all approved auditors
function getAuditors() external view override returns (address[] memory) {
return _auditorsSet.values();
}
/// @notice Returns `auditor`'s name
function getAuditorName(address auditor) external view override returns (string memory) {
return _auditorNames[auditor];
}
/// @notice Adds `auditor` to the list of approved auditors
/// @dev Can only be called by the owner
/// @dev Reverts if `auditor` is zero address
function addAuditor(address auditor, string memory name) external override onlyOwner nonZeroAddress(auditor) {
if (!_auditorsSet.add(auditor)) return;
_auditorNames[auditor] = name;
emit AddAuditor(auditor, name);
}
/// @notice Removes `auditor` from the list of approved auditors
/// @dev Can only be called by the owner
function removeAuditor(address auditor) external override onlyOwner {
if (!_auditorsSet.remove(auditor)) return;
delete _auditorNames[auditor];
emit RemoveAuditor(auditor);
}
// -------------------- //
// FORBIDDING INIT CODE //
// -------------------- //
/// @notice Whether init code with `initCodeHash` is forbidden
function isInitCodeForbidden(bytes32 initCodeHash) external view override returns (bool) {
return _isInitCodeForbidden[initCodeHash];
}
/// @notice Permanently marks init code with `initCodeHash` as forbidden
/// @dev Can only be called by the owner
function forbidInitCode(bytes32 initCodeHash) external override onlyOwner {
if (_isInitCodeForbidden[initCodeHash]) return;
_isInitCodeForbidden[initCodeHash] = true;
emit ForbidInitCode(initCodeHash);
}
/// @dev Reverts if `initCode` is forbidden
function _revertIfInitCodeIsForbidden(bytes memory initCode) internal view {
bytes32 initCodeHash = keccak256(initCode);
if (_isInitCodeForbidden[initCodeHash]) revert InitCodeIsForbiddenException(initCodeHash);
}
// ------------------------ //
// TOKENS WITH CUSTOM LOGIC //
// ------------------------ //
/// @notice Returns `token`'s specific postfix, if any
function getTokenSpecificPostfix(address token) external view override returns (bytes32) {
return _tokenSpecificPostfixes[token];
}
/// @notice Sets `token`'s specific `postfix`
/// @dev Can only be called by the owner
/// @dev Reverts if `postfix` is invalid
function setTokenSpecificPostfix(address token, bytes32 postfix) external override onlyOwner {
_validatePostfix(postfix);
if (_tokenSpecificPostfixes[token] == postfix) return;
_tokenSpecificPostfixes[token] = postfix;
emit SetTokenSpecificPostfix(token, postfix);
}
// --------------- //
// VERSION CONTROL //
// --------------- //
/// @notice Returns all versions for `cType`
function getVersions(bytes32 cType) external view override returns (uint256[] memory) {
return _versionInfo[cType].versionsSet.values();
}
/// @notice Returns the latest known bytecode version for given `cType`
/// @dev Reverts if `cType` has no bytecode entries
function getLatestVersion(bytes32 cType) external view override returns (uint256 ver) {
ver = _versionInfo[cType].latest;
if (ver == 0) revert VersionNotFoundException(cType);
}
/// @notice Returns the latest known version for given `cType` with matching `majorVersion`
/// @dev Reverts if `majorVersion` is less than `100` or greater than `999`
/// @dev Reverts if `cType` has no bytecode entries with matching `majorVersion`
function getLatestMinorVersion(bytes32 cType, uint256 majorVersion) external view override returns (uint256 ver) {
_validateVersion(cType, majorVersion);
ver = _versionInfo[cType].latestByMajor[_getMajorVersion(majorVersion)];
if (ver == 0) revert VersionNotFoundException(cType);
}
/// @notice Returns the latest known version for given `cType` with matching `minorVersion`
/// @dev Reverts if `minorVersion` is less than `100` or greater than `999`
/// @dev Reverts if `cType` has no bytecode entries with matching `minorVersion`
function getLatestPatchVersion(bytes32 cType, uint256 minorVersion) external view override returns (uint256 ver) {
_validateVersion(cType, minorVersion);
ver = _versionInfo[cType].latestByMinor[_getMinorVersion(minorVersion)];
if (ver == 0) revert VersionNotFoundException(cType);
}
/// @dev Updates version info for `cType` based on `ver`
function _updateVersionInfo(bytes32 cType, uint256 ver) internal {
VersionInfo storage info = _versionInfo[cType];
if (ver > info.latest) info.latest = ver;
uint256 majorVersion = _getMajorVersion(ver);
if (ver > info.latestByMajor[majorVersion]) info.latestByMajor[majorVersion] = ver;
uint256 minorVersion = _getMinorVersion(ver);
if (ver > info.latestByMinor[minorVersion]) info.latestByMinor[minorVersion] = ver;
info.versionsSet.add(ver);
}
/// @dev Returns the major version of a given version
function _getMajorVersion(uint256 ver) internal pure returns (uint256) {
return ver - ver % 100;
}
/// @dev Returns the minor version of a given version
function _getMinorVersion(uint256 ver) internal pure returns (uint256) {
return ver - ver % 10;
}
/// @dev Reverts if `cType` is invalid
function _validateContractType(bytes32 cType) internal pure {
if (!cType.isValidContractType()) revert InvalidContractTypeException(cType);
}
/// @dev Reverts if `domain` is invalid
function _validateDomain(bytes32 domain) internal pure {
if (!domain.isValidDomain()) revert InvalidDomainException(domain);
}
/// @dev Reverts if `postfix` is invalid
function _validatePostfix(bytes32 postfix) internal pure {
if (!postfix.isValidPostfix()) revert InvalidPostfixException(postfix);
}
/// @dev Reverts if `ver` is less than `100` or greater than `999`
function _validateVersion(bytes32 cType, uint256 ver) internal pure {
if (ver < 100 || ver > 999) revert InvalidVersionException(cType, ver);
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Create2.sol)
pragma solidity ^0.8.0;
/**
* @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer.
* `CREATE2` can be used to compute in advance the address where a smart
* contract will be deployed, which allows for interesting new mechanisms known
* as 'counterfactual interactions'.
*
* See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more
* information.
*/
library Create2 {
/**
* @dev Deploys a contract using `CREATE2`. The address where the contract
* will be deployed can be known in advance via {computeAddress}.
*
* The bytecode for a contract can be obtained from Solidity with
* `type(contractName).creationCode`.
*
* Requirements:
*
* - `bytecode` must not be empty.
* - `salt` must have not been used for `bytecode` already.
* - the factory must have a balance of at least `amount`.
* - if `amount` is non-zero, `bytecode` must have a `payable` constructor.
*/
function deploy(uint256 amount, bytes32 salt, bytes memory bytecode) internal returns (address addr) {
require(address(this).balance >= amount, "Create2: insufficient balance");
require(bytecode.length != 0, "Create2: bytecode length is zero");
/// @solidity memory-safe-assembly
assembly {
addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt)
}
require(addr != address(0), "Create2: Failed on deploy");
}
/**
* @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the
* `bytecodeHash` or `salt` will result in a new destination address.
*/
function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) {
return computeAddress(salt, bytecodeHash, address(this));
}
/**
* @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at
* `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}.
*/
function computeAddress(bytes32 salt, bytes32 bytecodeHash, address deployer) internal pure returns (address addr) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40) // Get free memory pointer
// | | ↓ ptr ... ↓ ptr + 0x0B (start) ... ↓ ptr + 0x20 ... ↓ ptr + 0x40 ... |
// |-------------------|---------------------------------------------------------------------------|
// | bytecodeHash | CCCCCCCCCCCCC...CC |
// | salt | BBBBBBBBBBBBB...BB |
// | deployer | 000000...0000AAAAAAAAAAAAAAAAAAA...AA |
// | 0xFF | FF |
// |-------------------|---------------------------------------------------------------------------|
// | memory | 000000...00FFAAAAAAAAAAAAAAAAAAA...AABBBBBBBBBBBBB...BBCCCCCCCCCCCCC...CC |
// | keccak(start, 85) | ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ |
mstore(add(ptr, 0x40), bytecodeHash)
mstore(add(ptr, 0x20), salt)
mstore(ptr, deployer) // Right-aligned with 12 preceding garbage bytes
let start := add(ptr, 0x0b) // The hashed data starts at the final garbage byte which we will set to 0xff
mstore8(start, 0xff)
addr := keccak256(start, 85)
}
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/ECDSA.sol)
pragma solidity ^0.8.0;
import "../Strings.sol";
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS,
InvalidSignatureV // Deprecated in v4.8
}
function _throwError(RecoverError error) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert("ECDSA: invalid signature");
} else if (error == RecoverError.InvalidSignatureLength) {
revert("ECDSA: invalid signature length");
} else if (error == RecoverError.InvalidSignatureS) {
revert("ECDSA: invalid signature 's' value");
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature` or error string. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
/// @solidity memory-safe-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength);
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, signature);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError) {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
uint8 v = uint8((uint256(vs) >> 255) + 27);
return tryRecover(hash, v, r, s);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*
* _Available since v4.2._
*/
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, r, vs);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address, RecoverError) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature);
}
return (signer, RecoverError.NoError);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, v, r, s);
_throwError(error);
return recovered;
}
/**
* @dev Returns an Ethereum Signed Message, created from a `hash`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 message) {
// 32 is the length in bytes of hash,
// enforced by the type signature above
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, "\x19Ethereum Signed Message:\n32")
mstore(0x1c, hash)
message := keccak256(0x00, 0x3c)
}
}
/**
* @dev Returns an Ethereum Signed Message, created from `s`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
}
/**
* @dev Returns an Ethereum Signed Typed Data, created from a
* `domainSeparator` and a `structHash`. This produces hash corresponding
* to the one signed with the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
* JSON-RPC method as part of EIP-712.
*
* See {recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 data) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40)
mstore(ptr, "\x19\x01")
mstore(add(ptr, 0x02), domainSeparator)
mstore(add(ptr, 0x22), structHash)
data := keccak256(ptr, 0x42)
}
}
/**
* @dev Returns an Ethereum Signed Data with intended validator, created from a
* `validator` and `data` according to the version 0 of EIP-191.
*
* See {recover}.
*/
function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x00", validator, data));
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.0;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position of the value in the `values` array, plus 1 because index 0
// means a value is not in the set.
mapping(bytes32 => uint256) _indexes;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slot
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
if (lastIndex != toDeleteIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the last value to the index where the value to delete is
set._values[toDeleteIndex] = lastValue;
// Update the index for the moved value
set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the index for the deleted slot
delete set._indexes[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Library for converting numbers into strings and other string operations.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibString.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol)
///
/// @dev Note:
/// For performance and bytecode compactness, most of the string operations are restricted to
/// byte strings (7-bit ASCII), except where otherwise specified.
/// Usage of byte string operations on charsets with runes spanning two or more bytes
/// can lead to undefined behavior.
library LibString {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The length of the output is too small to contain all the hex digits.
error HexLengthInsufficient();
/// @dev The length of the string is more than 32 bytes.
error TooBigForSmallString();
/// @dev The input string must be a 7-bit ASCII.
error StringNot7BitASCII();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The constant returned when the `search` is not found in the string.
uint256 internal constant NOT_FOUND = type(uint256).max;
/// @dev Lookup for '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.
uint128 internal constant ALPHANUMERIC_7_BIT_ASCII = 0x7fffffe07fffffe03ff000000000000;
/// @dev Lookup for 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.
uint128 internal constant LETTERS_7_BIT_ASCII = 0x7fffffe07fffffe0000000000000000;
/// @dev Lookup for 'abcdefghijklmnopqrstuvwxyz'.
uint128 internal constant LOWERCASE_7_BIT_ASCII = 0x7fffffe000000000000000000000000;
/// @dev Lookup for 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.
uint128 internal constant UPPERCASE_7_BIT_ASCII = 0x7fffffe0000000000000000;
/// @dev Lookup for '0123456789'.
uint128 internal constant DIGITS_7_BIT_ASCII = 0x3ff000000000000;
/// @dev Lookup for '0123456789abcdefABCDEF'.
uint128 internal constant HEXDIGITS_7_BIT_ASCII = 0x7e0000007e03ff000000000000;
/// @dev Lookup for '01234567'.
uint128 internal constant OCTDIGITS_7_BIT_ASCII = 0xff000000000000;
/// @dev Lookup for '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c'.
uint128 internal constant PRINTABLE_7_BIT_ASCII = 0x7fffffffffffffffffffffff00003e00;
/// @dev Lookup for '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'.
uint128 internal constant PUNCTUATION_7_BIT_ASCII = 0x78000001f8000001fc00fffe00000000;
/// @dev Lookup for ' \t\n\r\x0b\x0c'.
uint128 internal constant WHITESPACE_7_BIT_ASCII = 0x100003e00;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* DECIMAL OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the base 10 decimal representation of `value`.
function toString(uint256 value) internal pure returns (string memory str) {
/// @solidity memory-safe-assembly
assembly {
// The maximum value of a uint256 contains 78 digits (1 byte per digit), but
// we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.
// We will need 1 word for the trailing zeros padding, 1 word for the length,
// and 3 words for a maximum of 78 digits.
str := add(mload(0x40), 0x80)
// Update the free memory pointer to allocate.
mstore(0x40, add(str, 0x20))
// Zeroize the slot after the string.
mstore(str, 0)
// Cache the end of the memory to calculate the length later.
let end := str
let w := not(0) // Tsk.
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
for { let temp := value } 1 {} {
str := add(str, w) // `sub(str, 1)`.
// Write the character to the pointer.
// The ASCII index of the '0' character is 48.
mstore8(str, add(48, mod(temp, 10)))
// Keep dividing `temp` until zero.
temp := div(temp, 10)
if iszero(temp) { break }
}
let length := sub(end, str)
// Move the pointer 32 bytes leftwards to make room for the length.
str := sub(str, 0x20)
// Store the length.
mstore(str, length)
}
}
/// @dev Returns the base 10 decimal representation of `value`.
function toString(int256 value) internal pure returns (string memory str) {
if (value >= 0) {
return toString(uint256(value));
}
unchecked {
str = toString(~uint256(value) + 1);
}
/// @solidity memory-safe-assembly
assembly {
// We still have some spare memory space on the left,
// as we have allocated 3 words (96 bytes) for up to 78 digits.
let length := mload(str) // Load the string length.
mstore(str, 0x2d) // Store the '-' character.
str := sub(str, 1) // Move back the string pointer by a byte.
mstore(str, add(length, 1)) // Update the string length.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* HEXADECIMAL OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the hexadecimal representation of `value`,
/// left-padded to an input length of `length` bytes.
/// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte,
/// giving a total length of `length * 2 + 2` bytes.
/// Reverts if `length` is too small for the output to contain all the digits.
function toHexString(uint256 value, uint256 length) internal pure returns (string memory str) {
str = toHexStringNoPrefix(value, length);
/// @solidity memory-safe-assembly
assembly {
let strLength := add(mload(str), 2) // Compute the length.
mstore(str, 0x3078) // Write the "0x" prefix.
str := sub(str, 2) // Move the pointer.
mstore(str, strLength) // Write the length.
}
}
/// @dev Returns the hexadecimal representation of `value`,
/// left-padded to an input length of `length` bytes.
/// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte,
/// giving a total length of `length * 2` bytes.
/// Reverts if `length` is too small for the output to contain all the digits.
function toHexStringNoPrefix(uint256 value, uint256 length)
internal
pure
returns (string memory str)
{
/// @solidity memory-safe-assembly
assembly {
// We need 0x20 bytes for the trailing zeros padding, `length * 2` bytes
// for the digits, 0x02 bytes for the prefix, and 0x20 bytes for the length.
// We add 0x20 to the total and round down to a multiple of 0x20.
// (0x20 + 0x20 + 0x02 + 0x20) = 0x62.
str := add(mload(0x40), and(add(shl(1, length), 0x42), not(0x1f)))
// Allocate the memory.
mstore(0x40, add(str, 0x20))
// Zeroize the slot after the string.
mstore(str, 0)
// Cache the end to calculate the length later.
let end := str
// Store "0123456789abcdef" in scratch space.
mstore(0x0f, 0x30313233343536373839616263646566)
let start := sub(str, add(length, length))
let w := not(1) // Tsk.
let temp := value
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
for {} 1 {} {
str := add(str, w) // `sub(str, 2)`.
mstore8(add(str, 1), mload(and(temp, 15)))
mstore8(str, mload(and(shr(4, temp), 15)))
temp := shr(8, temp)
if iszero(xor(str, start)) { break }
}
if temp {
mstore(0x00, 0x2194895a) // `HexLengthInsufficient()`.
revert(0x1c, 0x04)
}
// Compute the string's length.
let strLength := sub(end, str)
// Move the pointer and write the length.
str := sub(str, 0x20)
mstore(str, strLength)
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
/// As address are 20 bytes long, the output will left-padded to have
/// a length of `20 * 2 + 2` bytes.
function toHexString(uint256 value) internal pure returns (string memory str) {
str = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
let strLength := add(mload(str), 2) // Compute the length.
mstore(str, 0x3078) // Write the "0x" prefix.
str := sub(str, 2) // Move the pointer.
mstore(str, strLength) // Write the length.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is prefixed with "0x".
/// The output excludes leading "0" from the `toHexString` output.
/// `0x00: "0x0", 0x01: "0x1", 0x12: "0x12", 0x123: "0x123"`.
function toMinimalHexString(uint256 value) internal pure returns (string memory str) {
str = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present.
let strLength := add(mload(str), 2) // Compute the length.
mstore(add(str, o), 0x3078) // Write the "0x" prefix, accounting for leading zero.
str := sub(add(str, o), 2) // Move the pointer, accounting for leading zero.
mstore(str, sub(strLength, o)) // Write the length, accounting for leading zero.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output excludes leading "0" from the `toHexStringNoPrefix` output.
/// `0x00: "0", 0x01: "1", 0x12: "12", 0x123: "123"`.
function toMinimalHexStringNoPrefix(uint256 value) internal pure returns (string memory str) {
str = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present.
let strLength := mload(str) // Get the length.
str := add(str, o) // Move the pointer, accounting for leading zero.
mstore(str, sub(strLength, o)) // Write the length, accounting for leading zero.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is encoded using 2 hexadecimal digits per byte.
/// As address are 20 bytes long, the output will left-padded to have
/// a length of `20 * 2` bytes.
function toHexStringNoPrefix(uint256 value) internal pure returns (string memory str) {
/// @solidity memory-safe-assembly
assembly {
// We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
// 0x02 bytes for the prefix, and 0x40 bytes for the digits.
// The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x40) is 0xa0.
str := add(mload(0x40), 0x80)
// Allocate the memory.
mstore(0x40, add(str, 0x20))
// Zeroize the slot after the string.
mstore(str, 0)
// Cache the end to calculate the length later.
let end := str
// Store "0123456789abcdef" in scratch space.
mstore(0x0f, 0x30313233343536373839616263646566)
let w := not(1) // Tsk.
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
for { let temp := value } 1 {} {
str := add(str, w) // `sub(str, 2)`.
mstore8(add(str, 1), mload(and(temp, 15)))
mstore8(str, mload(and(shr(4, temp), 15)))
temp := shr(8, temp)
if iszero(temp) { break }
}
// Compute the string's length.
let strLength := sub(end, str)
// Move the pointer and write the length.
str := sub(str, 0x20)
mstore(str, strLength)
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is prefixed with "0x", encoded using 2 hexadecimal digits per byte,
/// and the alphabets are capitalized conditionally according to
/// https://eips.ethereum.org/EIPS/eip-55
function toHexStringChecksummed(address value) internal pure returns (string memory str) {
str = toHexString(value);
/// @solidity memory-safe-assembly
assembly {
let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...`
let o := add(str, 0x22)
let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... `
let t := shl(240, 136) // `0b10001000 << 240`
for { let i := 0 } 1 {} {
mstore(add(i, i), mul(t, byte(i, hashed)))
i := add(i, 1)
if eq(i, 20) { break }
}
mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask)))))
o := add(o, 0x20)
mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask)))))
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.
function toHexString(address value) internal pure returns (string memory str) {
str = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
let strLength := add(mload(str), 2) // Compute the length.
mstore(str, 0x3078) // Write the "0x" prefix.
str := sub(str, 2) // Move the pointer.
mstore(str, strLength) // Write the length.
}
}
/// @dev Returns the hexadecimal representation of `value`.
/// The output is encoded using 2 hexadecimal digits per byte.
function toHexStringNoPrefix(address value) internal pure returns (string memory str) {
/// @solidity memory-safe-assembly
assembly {
str := mload(0x40)
// Allocate the memory.
// We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,
// 0x02 bytes for the prefix, and 0x28 bytes for the digits.
// The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80.
mstore(0x40, add(str, 0x80))
// Store "0123456789abcdef" in scratch space.
mstore(0x0f, 0x30313233343536373839616263646566)
str := add(str, 2)
mstore(str, 40)
let o := add(str, 0x20)
mstore(add(o, 40), 0)
value := shl(96, value)
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
for { let i := 0 } 1 {} {
let p := add(o, add(i, i))
let temp := byte(i, value)
mstore8(add(p, 1), mload(and(temp, 15)))
mstore8(p, mload(shr(4, temp)))
i := add(i, 1)
if eq(i, 20) { break }
}
}
}
/// @dev Returns the hex encoded string from the raw bytes.
/// The output is encoded using 2 hexadecimal digits per byte.
function toHexString(bytes memory raw) internal pure returns (string memory str) {
str = toHexStringNoPrefix(raw);
/// @solidity memory-safe-assembly
assembly {
let strLength := add(mload(str), 2) // Compute the length.
mstore(str, 0x3078) // Write the "0x" prefix.
str := sub(str, 2) // Move the pointer.
mstore(str, strLength) // Write the length.
}
}
/// @dev Returns the hex encoded string from the raw bytes.
/// The output is encoded using 2 hexadecimal digits per byte.
function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory str) {
/// @solidity memory-safe-assembly
assembly {
let length := mload(raw)
str := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix.
mstore(str, add(length, length)) // Store the length of the output.
// Store "0123456789abcdef" in scratch space.
mstore(0x0f, 0x30313233343536373839616263646566)
let o := add(str, 0x20)
let end := add(raw, length)
for {} iszero(eq(raw, end)) {} {
raw := add(raw, 1)
mstore8(add(o, 1), mload(and(mload(raw), 15)))
mstore8(o, mload(and(shr(4, mload(raw)), 15)))
o := add(o, 2)
}
mstore(o, 0) // Zeroize the slot after the string.
mstore(0x40, add(o, 0x20)) // Allocate the memory.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* RUNE STRING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the number of UTF characters in the string.
function runeCount(string memory s) internal pure returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
if mload(s) {
mstore(0x00, div(not(0), 255))
mstore(0x20, 0x0202020202020202020202020202020202020202020202020303030304040506)
let o := add(s, 0x20)
let end := add(o, mload(s))
for { result := 1 } 1 { result := add(result, 1) } {
o := add(o, byte(0, mload(shr(250, mload(o)))))
if iszero(lt(o, end)) { break }
}
}
}
}
/// @dev Returns if this string is a 7-bit ASCII string.
/// (i.e. all characters codes are in [0..127])
function is7BitASCII(string memory s) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
let mask := shl(7, div(not(0), 255))
result := 1
let n := mload(s)
if n {
let o := add(s, 0x20)
let end := add(o, n)
let last := mload(end)
mstore(end, 0)
for {} 1 {} {
if and(mask, mload(o)) {
result := 0
break
}
o := add(o, 0x20)
if iszero(lt(o, end)) { break }
}
mstore(end, last)
}
}
}
/// @dev Returns if this string is a 7-bit ASCII string,
/// AND all characters are in the `allowed` lookup.
/// Note: If `s` is empty, returns true regardless of `allowed`.
function is7BitASCII(string memory s, uint128 allowed) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := 1
if mload(s) {
let allowed_ := shr(128, shl(128, allowed))
let o := add(s, 0x20)
let end := add(o, mload(s))
for {} 1 {} {
result := and(result, shr(byte(0, mload(o)), allowed_))
o := add(o, 1)
if iszero(and(result, lt(o, end))) { break }
}
}
}
}
/// @dev Converts the bytes in the 7-bit ASCII string `s` to
/// an allowed lookup for use in `is7BitASCII(s, allowed)`.
/// To save runtime gas, you can cache the result in an immutable variable.
function to7BitASCIIAllowedLookup(string memory s) internal pure returns (uint128 result) {
/// @solidity memory-safe-assembly
assembly {
if mload(s) {
let o := add(s, 0x20)
let end := add(o, mload(s))
for {} 1 {} {
result := or(result, shl(byte(0, mload(o)), 1))
o := add(o, 1)
if iszero(lt(o, end)) { break }
}
if shr(128, result) {
mstore(0x00, 0xc9807e0d) // `StringNot7BitASCII()`.
revert(0x1c, 0x04)
}
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* BYTE STRING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// For performance and bytecode compactness, byte string operations are restricted
// to 7-bit ASCII strings. All offsets are byte offsets, not UTF character offsets.
// Usage of byte string operations on charsets with runes spanning two or more bytes
// can lead to undefined behavior.
/// @dev Returns `subject` all occurrences of `search` replaced with `replacement`.
function replace(string memory subject, string memory search, string memory replacement)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
let subjectLength := mload(subject)
let searchLength := mload(search)
let replacementLength := mload(replacement)
subject := add(subject, 0x20)
search := add(search, 0x20)
replacement := add(replacement, 0x20)
result := add(mload(0x40), 0x20)
let subjectEnd := add(subject, subjectLength)
if iszero(gt(searchLength, subjectLength)) {
let subjectSearchEnd := add(sub(subjectEnd, searchLength), 1)
let h := 0
if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }
let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
let s := mload(search)
for {} 1 {} {
let t := mload(subject)
// Whether the first `searchLength % 32` bytes of
// `subject` and `search` matches.
if iszero(shr(m, xor(t, s))) {
if h {
if iszero(eq(keccak256(subject, searchLength), h)) {
mstore(result, t)
result := add(result, 1)
subject := add(subject, 1)
if iszero(lt(subject, subjectSearchEnd)) { break }
continue
}
}
// Copy the `replacement` one word at a time.
for { let o := 0 } 1 {} {
mstore(add(result, o), mload(add(replacement, o)))
o := add(o, 0x20)
if iszero(lt(o, replacementLength)) { break }
}
result := add(result, replacementLength)
subject := add(subject, searchLength)
if searchLength {
if iszero(lt(subject, subjectSearchEnd)) { break }
continue
}
}
mstore(result, t)
result := add(result, 1)
subject := add(subject, 1)
if iszero(lt(subject, subjectSearchEnd)) { break }
}
}
let resultRemainder := result
result := add(mload(0x40), 0x20)
let k := add(sub(resultRemainder, result), sub(subjectEnd, subject))
// Copy the rest of the string one word at a time.
for {} lt(subject, subjectEnd) {} {
mstore(resultRemainder, mload(subject))
resultRemainder := add(resultRemainder, 0x20)
subject := add(subject, 0x20)
}
result := sub(result, 0x20)
let last := add(add(result, 0x20), k) // Zeroize the slot after the string.
mstore(last, 0)
mstore(0x40, add(last, 0x20)) // Allocate the memory.
mstore(result, k) // Store the length.
}
}
/// @dev Returns the byte index of the first location of `search` in `subject`,
/// searching from left to right, starting from `from`.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
function indexOf(string memory subject, string memory search, uint256 from)
internal
pure
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
for { let subjectLength := mload(subject) } 1 {} {
if iszero(mload(search)) {
if iszero(gt(from, subjectLength)) {
result := from
break
}
result := subjectLength
break
}
let searchLength := mload(search)
let subjectStart := add(subject, 0x20)
result := not(0) // Initialize to `NOT_FOUND`.
subject := add(subjectStart, from)
let end := add(sub(add(subjectStart, subjectLength), searchLength), 1)
let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
let s := mload(add(search, 0x20))
if iszero(and(lt(subject, end), lt(from, subjectLength))) { break }
if iszero(lt(searchLength, 0x20)) {
for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {
if iszero(shr(m, xor(mload(subject), s))) {
if eq(keccak256(subject, searchLength), h) {
result := sub(subject, subjectStart)
break
}
}
subject := add(subject, 1)
if iszero(lt(subject, end)) { break }
}
break
}
for {} 1 {} {
if iszero(shr(m, xor(mload(subject), s))) {
result := sub(subject, subjectStart)
break
}
subject := add(subject, 1)
if iszero(lt(subject, end)) { break }
}
break
}
}
}
/// @dev Returns the byte index of the first location of `search` in `subject`,
/// searching from left to right.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
function indexOf(string memory subject, string memory search)
internal
pure
returns (uint256 result)
{
result = indexOf(subject, search, 0);
}
/// @dev Returns the byte index of the first location of `search` in `subject`,
/// searching from right to left, starting from `from`.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
function lastIndexOf(string memory subject, string memory search, uint256 from)
internal
pure
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
for {} 1 {} {
result := not(0) // Initialize to `NOT_FOUND`.
let searchLength := mload(search)
if gt(searchLength, mload(subject)) { break }
let w := result
let fromMax := sub(mload(subject), searchLength)
if iszero(gt(fromMax, from)) { from := fromMax }
let end := add(add(subject, 0x20), w)
subject := add(add(subject, 0x20), from)
if iszero(gt(subject, end)) { break }
// As this function is not too often used,
// we shall simply use keccak256 for smaller bytecode size.
for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {
if eq(keccak256(subject, searchLength), h) {
result := sub(subject, add(end, 1))
break
}
subject := add(subject, w) // `sub(subject, 1)`.
if iszero(gt(subject, end)) { break }
}
break
}
}
}
/// @dev Returns the byte index of the first location of `search` in `subject`,
/// searching from right to left.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
function lastIndexOf(string memory subject, string memory search)
internal
pure
returns (uint256 result)
{
result = lastIndexOf(subject, search, uint256(int256(-1)));
}
/// @dev Returns true if `search` is found in `subject`, false otherwise.
function contains(string memory subject, string memory search) internal pure returns (bool) {
return indexOf(subject, search) != NOT_FOUND;
}
/// @dev Returns whether `subject` starts with `search`.
function startsWith(string memory subject, string memory search)
internal
pure
returns (bool result)
{
/// @solidity memory-safe-assembly
assembly {
let searchLength := mload(search)
// Just using keccak256 directly is actually cheaper.
// forgefmt: disable-next-item
result := and(
iszero(gt(searchLength, mload(subject))),
eq(
keccak256(add(subject, 0x20), searchLength),
keccak256(add(search, 0x20), searchLength)
)
)
}
}
/// @dev Returns whether `subject` ends with `search`.
function endsWith(string memory subject, string memory search)
internal
pure
returns (bool result)
{
/// @solidity memory-safe-assembly
assembly {
let searchLength := mload(search)
let subjectLength := mload(subject)
// Whether `search` is not longer than `subject`.
let withinRange := iszero(gt(searchLength, subjectLength))
// Just using keccak256 directly is actually cheaper.
// forgefmt: disable-next-item
result := and(
withinRange,
eq(
keccak256(
// `subject + 0x20 + max(subjectLength - searchLength, 0)`.
add(add(subject, 0x20), mul(withinRange, sub(subjectLength, searchLength))),
searchLength
),
keccak256(add(search, 0x20), searchLength)
)
)
}
}
/// @dev Returns `subject` repeated `times`.
function repeat(string memory subject, uint256 times)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
let subjectLength := mload(subject)
if iszero(or(iszero(times), iszero(subjectLength))) {
subject := add(subject, 0x20)
result := mload(0x40)
let output := add(result, 0x20)
for {} 1 {} {
// Copy the `subject` one word at a time.
for { let o := 0 } 1 {} {
mstore(add(output, o), mload(add(subject, o)))
o := add(o, 0x20)
if iszero(lt(o, subjectLength)) { break }
}
output := add(output, subjectLength)
times := sub(times, 1)
if iszero(times) { break }
}
mstore(output, 0) // Zeroize the slot after the string.
let resultLength := sub(output, add(result, 0x20))
mstore(result, resultLength) // Store the length.
// Allocate the memory.
mstore(0x40, add(result, add(resultLength, 0x20)))
}
}
}
/// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive).
/// `start` and `end` are byte offsets.
function slice(string memory subject, uint256 start, uint256 end)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
let subjectLength := mload(subject)
if iszero(gt(subjectLength, end)) { end := subjectLength }
if iszero(gt(subjectLength, start)) { start := subjectLength }
if lt(start, end) {
result := mload(0x40)
let resultLength := sub(end, start)
mstore(result, resultLength)
subject := add(subject, start)
let w := not(0x1f)
// Copy the `subject` one word at a time, backwards.
for { let o := and(add(resultLength, 0x1f), w) } 1 {} {
mstore(add(result, o), mload(add(subject, o)))
o := add(o, w) // `sub(o, 0x20)`.
if iszero(o) { break }
}
// Zeroize the slot after the string.
mstore(add(add(result, 0x20), resultLength), 0)
// Allocate memory for the length and the bytes,
// rounded up to a multiple of 32.
mstore(0x40, add(result, and(add(resultLength, 0x3f), w)))
}
}
}
/// @dev Returns a copy of `subject` sliced from `start` to the end of the string.
/// `start` is a byte offset.
function slice(string memory subject, uint256 start)
internal
pure
returns (string memory result)
{
result = slice(subject, start, uint256(int256(-1)));
}
/// @dev Returns all the indices of `search` in `subject`.
/// The indices are byte offsets.
function indicesOf(string memory subject, string memory search)
internal
pure
returns (uint256[] memory result)
{
/// @solidity memory-safe-assembly
assembly {
let subjectLength := mload(subject)
let searchLength := mload(search)
if iszero(gt(searchLength, subjectLength)) {
subject := add(subject, 0x20)
search := add(search, 0x20)
result := add(mload(0x40), 0x20)
let subjectStart := subject
let subjectSearchEnd := add(sub(add(subject, subjectLength), searchLength), 1)
let h := 0
if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }
let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
let s := mload(search)
for {} 1 {} {
let t := mload(subject)
// Whether the first `searchLength % 32` bytes of
// `subject` and `search` matches.
if iszero(shr(m, xor(t, s))) {
if h {
if iszero(eq(keccak256(subject, searchLength), h)) {
subject := add(subject, 1)
if iszero(lt(subject, subjectSearchEnd)) { break }
continue
}
}
// Append to `result`.
mstore(result, sub(subject, subjectStart))
result := add(result, 0x20)
// Advance `subject` by `searchLength`.
subject := add(subject, searchLength)
if searchLength {
if iszero(lt(subject, subjectSearchEnd)) { break }
continue
}
}
subject := add(subject, 1)
if iszero(lt(subject, subjectSearchEnd)) { break }
}
let resultEnd := result
// Assign `result` to the free memory pointer.
result := mload(0x40)
// Store the length of `result`.
mstore(result, shr(5, sub(resultEnd, add(result, 0x20))))
// Allocate memory for result.
// We allocate one more word, so this array can be recycled for {split}.
mstore(0x40, add(resultEnd, 0x20))
}
}
}
/// @dev Returns a arrays of strings based on the `delimiter` inside of the `subject` string.
function split(string memory subject, string memory delimiter)
internal
pure
returns (string[] memory result)
{
uint256[] memory indices = indicesOf(subject, delimiter);
/// @solidity memory-safe-assembly
assembly {
let w := not(0x1f)
let indexPtr := add(indices, 0x20)
let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1)))
mstore(add(indicesEnd, w), mload(subject))
mstore(indices, add(mload(indices), 1))
let prevIndex := 0
for {} 1 {} {
let index := mload(indexPtr)
mstore(indexPtr, 0x60)
if iszero(eq(index, prevIndex)) {
let element := mload(0x40)
let elementLength := sub(index, prevIndex)
mstore(element, elementLength)
// Copy the `subject` one word at a time, backwards.
for { let o := and(add(elementLength, 0x1f), w) } 1 {} {
mstore(add(element, o), mload(add(add(subject, prevIndex), o)))
o := add(o, w) // `sub(o, 0x20)`.
if iszero(o) { break }
}
// Zeroize the slot after the string.
mstore(add(add(element, 0x20), elementLength), 0)
// Allocate memory for the length and the bytes,
// rounded up to a multiple of 32.
mstore(0x40, add(element, and(add(elementLength, 0x3f), w)))
// Store the `element` into the array.
mstore(indexPtr, element)
}
prevIndex := add(index, mload(delimiter))
indexPtr := add(indexPtr, 0x20)
if iszero(lt(indexPtr, indicesEnd)) { break }
}
result := indices
if iszero(mload(delimiter)) {
result := add(indices, 0x20)
mstore(result, sub(mload(indices), 2))
}
}
}
/// @dev Returns a concatenated string of `a` and `b`.
/// Cheaper than `string.concat()` and does not de-align the free memory pointer.
function concat(string memory a, string memory b)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
let w := not(0x1f)
result := mload(0x40)
let aLength := mload(a)
// Copy `a` one word at a time, backwards.
for { let o := and(add(aLength, 0x20), w) } 1 {} {
mstore(add(result, o), mload(add(a, o)))
o := add(o, w) // `sub(o, 0x20)`.
if iszero(o) { break }
}
let bLength := mload(b)
let output := add(result, aLength)
// Copy `b` one word at a time, backwards.
for { let o := and(add(bLength, 0x20), w) } 1 {} {
mstore(add(output, o), mload(add(b, o)))
o := add(o, w) // `sub(o, 0x20)`.
if iszero(o) { break }
}
let totalLength := add(aLength, bLength)
let last := add(add(result, 0x20), totalLength)
// Zeroize the slot after the string.
mstore(last, 0)
// Stores the length.
mstore(result, totalLength)
// Allocate memory for the length and the bytes,
// rounded up to a multiple of 32.
mstore(0x40, and(add(last, 0x1f), w))
}
}
/// @dev Returns a copy of the string in either lowercase or UPPERCASE.
/// WARNING! This function is only compatible with 7-bit ASCII strings.
function toCase(string memory subject, bool toUpper)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
let length := mload(subject)
if length {
result := add(mload(0x40), 0x20)
subject := add(subject, 1)
let flags := shl(add(70, shl(5, toUpper)), 0x3ffffff)
let w := not(0)
for { let o := length } 1 {} {
o := add(o, w)
let b := and(0xff, mload(add(subject, o)))
mstore8(add(result, o), xor(b, and(shr(b, flags), 0x20)))
if iszero(o) { break }
}
result := mload(0x40)
mstore(result, length) // Store the length.
let last := add(add(result, 0x20), length)
mstore(last, 0) // Zeroize the slot after the string.
mstore(0x40, add(last, 0x20)) // Allocate the memory.
}
}
}
/// @dev Returns a string from a small bytes32 string.
/// `s` must be null-terminated, or behavior will be undefined.
function fromSmallString(bytes32 s) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
let n := 0
for {} byte(n, s) { n := add(n, 1) } {} // Scan for '\0'.
mstore(result, n)
let o := add(result, 0x20)
mstore(o, s)
mstore(add(o, n), 0)
mstore(0x40, add(result, 0x40))
}
}
/// @dev Returns the small string, with all bytes after the first null byte zeroized.
function normalizeSmallString(bytes32 s) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
for {} byte(result, s) { result := add(result, 1) } {} // Scan for '\0'.
mstore(0x00, s)
mstore(result, 0x00)
result := mload(0x00)
}
}
/// @dev Returns the string as a normalized null-terminated small string.
function toSmallString(string memory s) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(s)
if iszero(lt(result, 33)) {
mstore(0x00, 0xec92f9a3) // `TooBigForSmallString()`.
revert(0x1c, 0x04)
}
result := shl(shl(3, sub(32, result)), mload(add(s, result)))
}
}
/// @dev Returns a lowercased copy of the string.
/// WARNING! This function is only compatible with 7-bit ASCII strings.
function lower(string memory subject) internal pure returns (string memory result) {
result = toCase(subject, false);
}
/// @dev Returns an UPPERCASED copy of the string.
/// WARNING! This function is only compatible with 7-bit ASCII strings.
function upper(string memory subject) internal pure returns (string memory result) {
result = toCase(subject, true);
}
/// @dev Escapes the string to be used within HTML tags.
function escapeHTML(string memory s) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
let end := add(s, mload(s))
result := add(mload(0x40), 0x20)
// Store the bytes of the packed offsets and strides into the scratch space.
// `packed = (stride << 5) | offset`. Max offset is 20. Max stride is 6.
mstore(0x1f, 0x900094)
mstore(0x08, 0xc0000000a6ab)
// Store ""&'<>" into the scratch space.
mstore(0x00, shl(64, 0x2671756f743b26616d703b262333393b266c743b2667743b))
for {} iszero(eq(s, end)) {} {
s := add(s, 1)
let c := and(mload(s), 0xff)
// Not in `["\"","'","&","<",">"]`.
if iszero(and(shl(c, 1), 0x500000c400000000)) {
mstore8(result, c)
result := add(result, 1)
continue
}
let t := shr(248, mload(c))
mstore(result, mload(and(t, 0x1f)))
result := add(result, shr(5, t))
}
let last := result
mstore(last, 0) // Zeroize the slot after the string.
result := mload(0x40)
mstore(result, sub(last, add(result, 0x20))) // Store the length.
mstore(0x40, add(last, 0x20)) // Allocate the memory.
}
}
/// @dev Escapes the string to be used within double-quotes in a JSON.
/// If `addDoubleQuotes` is true, the result will be enclosed in double-quotes.
function escapeJSON(string memory s, bool addDoubleQuotes)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
let end := add(s, mload(s))
result := add(mload(0x40), 0x20)
if addDoubleQuotes {
mstore8(result, 34)
result := add(1, result)
}
// Store "\\u0000" in scratch space.
// Store "0123456789abcdef" in scratch space.
// Also, store `{0x08:"b", 0x09:"t", 0x0a:"n", 0x0c:"f", 0x0d:"r"}`.
// into the scratch space.
mstore(0x15, 0x5c75303030303031323334353637383961626364656662746e006672)
// Bitmask for detecting `["\"","\\"]`.
let e := or(shl(0x22, 1), shl(0x5c, 1))
for {} iszero(eq(s, end)) {} {
s := add(s, 1)
let c := and(mload(s), 0xff)
if iszero(lt(c, 0x20)) {
if iszero(and(shl(c, 1), e)) {
// Not in `["\"","\\"]`.
mstore8(result, c)
result := add(result, 1)
continue
}
mstore8(result, 0x5c) // "\\".
mstore8(add(result, 1), c)
result := add(result, 2)
continue
}
if iszero(and(shl(c, 1), 0x3700)) {
// Not in `["\b","\t","\n","\f","\d"]`.
mstore8(0x1d, mload(shr(4, c))) // Hex value.
mstore8(0x1e, mload(and(c, 15))) // Hex value.
mstore(result, mload(0x19)) // "\\u00XX".
result := add(result, 6)
continue
}
mstore8(result, 0x5c) // "\\".
mstore8(add(result, 1), mload(add(c, 8)))
result := add(result, 2)
}
if addDoubleQuotes {
mstore8(result, 34)
result := add(1, result)
}
let last := result
mstore(last, 0) // Zeroize the slot after the string.
result := mload(0x40)
mstore(result, sub(last, add(result, 0x20))) // Store the length.
mstore(0x40, add(last, 0x20)) // Allocate the memory.
}
}
/// @dev Escapes the string to be used within double-quotes in a JSON.
function escapeJSON(string memory s) internal pure returns (string memory result) {
result = escapeJSON(s, false);
}
/// @dev Returns whether `a` equals `b`.
function eq(string memory a, string memory b) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b)))
}
}
/// @dev Returns whether `a` equals `b`, where `b` is a null-terminated small string.
function eqs(string memory a, bytes32 b) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
// These should be evaluated on compile time, as far as possible.
let m := not(shl(7, div(not(iszero(b)), 255))) // `0x7f7f ...`.
let x := not(or(m, or(b, add(m, and(b, m)))))
let r := shl(7, iszero(iszero(shr(128, x))))
r := or(r, shl(6, iszero(iszero(shr(64, shr(r, x))))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
// forgefmt: disable-next-item
result := gt(eq(mload(a), add(iszero(x), xor(31, shr(3, r)))),
xor(shr(add(8, r), b), shr(add(8, r), mload(add(a, 0x20)))))
}
}
/// @dev Packs a single string with its length into a single word.
/// Returns `bytes32(0)` if the length is zero or greater than 31.
function packOne(string memory a) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
// We don't need to zero right pad the string,
// since this is our own custom non-standard packing scheme.
result :=
mul(
// Load the length and the bytes.
mload(add(a, 0x1f)),
// `length != 0 && length < 32`. Abuses underflow.
// Assumes that the length is valid and within the block gas limit.
lt(sub(mload(a), 1), 0x1f)
)
}
}
/// @dev Unpacks a string packed using {packOne}.
/// Returns the empty string if `packed` is `bytes32(0)`.
/// If `packed` is not an output of {packOne}, the output behavior is undefined.
function unpackOne(bytes32 packed) internal pure returns (string memory result) {
/// @solidity memory-safe-assembly
assembly {
// Grab the free memory pointer.
result := mload(0x40)
// Allocate 2 words (1 for the length, 1 for the bytes).
mstore(0x40, add(result, 0x40))
// Zeroize the length slot.
mstore(result, 0)
// Store the length and bytes.
mstore(add(result, 0x1f), packed)
// Right pad with zeroes.
mstore(add(add(result, 0x20), mload(result)), 0)
}
}
/// @dev Packs two strings with their lengths into a single word.
/// Returns `bytes32(0)` if combined length is zero or greater than 30.
function packTwo(string memory a, string memory b) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let aLength := mload(a)
// We don't need to zero right pad the strings,
// since this is our own custom non-standard packing scheme.
result :=
mul(
// Load the length and the bytes of `a` and `b`.
or(
shl(shl(3, sub(0x1f, aLength)), mload(add(a, aLength))),
mload(sub(add(b, 0x1e), aLength))
),
// `totalLength != 0 && totalLength < 31`. Abuses underflow.
// Assumes that the lengths are valid and within the block gas limit.
lt(sub(add(aLength, mload(b)), 1), 0x1e)
)
}
}
/// @dev Unpacks strings packed using {packTwo}.
/// Returns the empty strings if `packed` is `bytes32(0)`.
/// If `packed` is not an output of {packTwo}, the output behavior is undefined.
function unpackTwo(bytes32 packed)
internal
pure
returns (string memory resultA, string memory resultB)
{
/// @solidity memory-safe-assembly
assembly {
// Grab the free memory pointer.
resultA := mload(0x40)
resultB := add(resultA, 0x40)
// Allocate 2 words for each string (1 for the length, 1 for the byte). Total 4 words.
mstore(0x40, add(resultB, 0x40))
// Zeroize the length slots.
mstore(resultA, 0)
mstore(resultB, 0)
// Store the lengths and bytes.
mstore(add(resultA, 0x1f), packed)
mstore(add(resultB, 0x1f), mload(add(add(resultA, 0x20), mload(resultA))))
// Right pad with zeroes.
mstore(add(add(resultA, 0x20), mload(resultA)), 0)
mstore(add(add(resultB, 0x20), mload(resultB)), 0)
}
}
/// @dev Directly returns `a` without copying.
function directReturn(string memory a) internal pure {
assembly {
// Assumes that the string does not start from the scratch space.
let retStart := sub(a, 0x20)
let retUnpaddedSize := add(mload(a), 0x40)
// Right pad with zeroes. Just in case the string is produced
// by a method that doesn't zero right pad.
mstore(add(retStart, retUnpaddedSize), 0)
// Store the return offset.
mstore(retStart, 0x20)
// End the transaction, returning the string.
return(retStart, and(not(0x1f), add(0x1f, retUnpaddedSize)))
}
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Read and write to persistent storage at a fraction of the cost.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SSTORE2.sol)
/// @author Saw-mon-and-Natalie (https://github.com/Saw-mon-and-Natalie)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SSTORE2.sol)
/// @author Modified from 0xSequence (https://github.com/0xSequence/sstore2/blob/master/contracts/SSTORE2.sol)
library SSTORE2 {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev We skip the first byte as it's a STOP opcode,
/// which ensures the contract can't be called.
uint256 internal constant DATA_OFFSET = 1;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Unable to deploy the storage contract.
error DeploymentFailed();
/// @dev The storage contract address is invalid.
error InvalidPointer();
/// @dev Attempt to read outside of the storage contract's bytecode bounds.
error ReadOutOfBounds();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* WRITE LOGIC */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Writes `data` into the bytecode of a storage contract and returns its address.
function write(bytes memory data) internal returns (address pointer) {
/// @solidity memory-safe-assembly
assembly {
let originalDataLength := mload(data)
// Add 1 to data size since we are prefixing it with a STOP opcode.
let dataSize := add(originalDataLength, DATA_OFFSET)
/**
* ------------------------------------------------------------------------------+
* Opcode | Mnemonic | Stack | Memory |
* ------------------------------------------------------------------------------|
* 61 dataSize | PUSH2 dataSize | dataSize | |
* 80 | DUP1 | dataSize dataSize | |
* 60 0xa | PUSH1 0xa | 0xa dataSize dataSize | |
* 3D | RETURNDATASIZE | 0 0xa dataSize dataSize | |
* 39 | CODECOPY | dataSize | [0..dataSize): code |
* 3D | RETURNDATASIZE | 0 dataSize | [0..dataSize): code |
* F3 | RETURN | | [0..dataSize): code |
* 00 | STOP | | |
* ------------------------------------------------------------------------------+
* @dev Prefix the bytecode with a STOP opcode to ensure it cannot be called.
* Also PUSH2 is used since max contract size cap is 24,576 bytes which is less than 2 ** 16.
*/
mstore(
// Do a out-of-gas revert if `dataSize` is more than 2 bytes.
// The actual EVM limit may be smaller and may change over time.
add(data, gt(dataSize, 0xffff)),
// Left shift `dataSize` by 64 so that it lines up with the 0000 after PUSH2.
or(0xfd61000080600a3d393df300, shl(0x40, dataSize))
)
// Deploy a new contract with the generated creation code.
pointer := create(0, add(data, 0x15), add(dataSize, 0xa))
// If `pointer` is zero, revert.
if iszero(pointer) {
// Store the function selector of `DeploymentFailed()`.
mstore(0x00, 0x30116425)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
// Restore original length of the variable size `data`.
mstore(data, originalDataLength)
}
}
/// @dev Writes `data` into the bytecode of a storage contract with `salt`
/// and returns its deterministic address.
function writeDeterministic(bytes memory data, bytes32 salt)
internal
returns (address pointer)
{
/// @solidity memory-safe-assembly
assembly {
let originalDataLength := mload(data)
let dataSize := add(originalDataLength, DATA_OFFSET)
mstore(
// Do a out-of-gas revert if `dataSize` is more than 2 bytes.
// The actual EVM limit may be smaller and may change over time.
add(data, gt(dataSize, 0xffff)),
// Left shift `dataSize` by 64 so that it lines up with the 0000 after PUSH2.
or(0xfd61000080600a3d393df300, shl(0x40, dataSize))
)
// Deploy a new contract with the generated creation code.
pointer := create2(0, add(data, 0x15), add(dataSize, 0xa), salt)
// If `pointer` is zero, revert.
if iszero(pointer) {
// Store the function selector of `DeploymentFailed()`.
mstore(0x00, 0x30116425)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
// Restore original length of the variable size `data`.
mstore(data, originalDataLength)
}
}
/// @dev Returns the initialization code hash of the storage contract for `data`.
/// Used for mining vanity addresses with create2crunch.
function initCodeHash(bytes memory data) internal pure returns (bytes32 hash) {
/// @solidity memory-safe-assembly
assembly {
let originalDataLength := mload(data)
let dataSize := add(originalDataLength, DATA_OFFSET)
// Do a out-of-gas revert if `dataSize` is more than 2 bytes.
// The actual EVM limit may be smaller and may change over time.
returndatacopy(returndatasize(), returndatasize(), shr(16, dataSize))
mstore(data, or(0x61000080600a3d393df300, shl(0x40, dataSize)))
hash := keccak256(add(data, 0x15), add(dataSize, 0xa))
// Restore original length of the variable size `data`.
mstore(data, originalDataLength)
}
}
/// @dev Returns the address of the storage contract for `data`
/// deployed with `salt` by `deployer`.
/// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
function predictDeterministicAddress(bytes memory data, bytes32 salt, address deployer)
internal
pure
returns (address predicted)
{
bytes32 hash = initCodeHash(data);
/// @solidity memory-safe-assembly
assembly {
// Compute and store the bytecode hash.
mstore8(0x00, 0xff) // Write the prefix.
mstore(0x35, hash)
mstore(0x01, shl(96, deployer))
mstore(0x15, salt)
predicted := keccak256(0x00, 0x55)
// Restore the part of the free memory pointer that has been overwritten.
mstore(0x35, 0)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* READ LOGIC */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns all the `data` from the bytecode of the storage contract at `pointer`.
function read(address pointer) internal view returns (bytes memory data) {
/// @solidity memory-safe-assembly
assembly {
let pointerCodesize := extcodesize(pointer)
if iszero(pointerCodesize) {
// Store the function selector of `InvalidPointer()`.
mstore(0x00, 0x11052bb4)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
// Offset all indices by 1 to skip the STOP opcode.
let size := sub(pointerCodesize, DATA_OFFSET)
// Get the pointer to the free memory and allocate
// enough 32-byte words for the data and the length of the data,
// then copy the code to the allocated memory.
// Masking with 0xffe0 will suffice, since contract size is less than 16 bits.
data := mload(0x40)
mstore(0x40, add(data, and(add(size, 0x3f), 0xffe0)))
mstore(data, size)
mstore(add(add(data, 0x20), size), 0) // Zeroize the last slot.
extcodecopy(pointer, add(data, 0x20), DATA_OFFSET, size)
}
}
/// @dev Returns the `data` from the bytecode of the storage contract at `pointer`,
/// from the byte at `start`, to the end of the data stored.
function read(address pointer, uint256 start) internal view returns (bytes memory data) {
/// @solidity memory-safe-assembly
assembly {
let pointerCodesize := extcodesize(pointer)
if iszero(pointerCodesize) {
// Store the function selector of `InvalidPointer()`.
mstore(0x00, 0x11052bb4)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
// If `!(pointer.code.size > start)`, reverts.
// This also handles the case where `start + DATA_OFFSET` overflows.
if iszero(gt(pointerCodesize, start)) {
// Store the function selector of `ReadOutOfBounds()`.
mstore(0x00, 0x84eb0dd1)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
let size := sub(pointerCodesize, add(start, DATA_OFFSET))
// Get the pointer to the free memory and allocate
// enough 32-byte words for the data and the length of the data,
// then copy the code to the allocated memory.
// Masking with 0xffe0 will suffice, since contract size is less than 16 bits.
data := mload(0x40)
mstore(0x40, add(data, and(add(size, 0x3f), 0xffe0)))
mstore(data, size)
mstore(add(add(data, 0x20), size), 0) // Zeroize the last slot.
extcodecopy(pointer, add(data, 0x20), add(start, DATA_OFFSET), size)
}
}
/// @dev Returns the `data` from the bytecode of the storage contract at `pointer`,
/// from the byte at `start`, to the byte at `end` (exclusive) of the data stored.
function read(address pointer, uint256 start, uint256 end)
internal
view
returns (bytes memory data)
{
/// @solidity memory-safe-assembly
assembly {
let pointerCodesize := extcodesize(pointer)
if iszero(pointerCodesize) {
// Store the function selector of `InvalidPointer()`.
mstore(0x00, 0x11052bb4)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
// If `!(pointer.code.size > end) || (start > end)`, revert.
// This also handles the cases where
// `end + DATA_OFFSET` or `start + DATA_OFFSET` overflows.
if iszero(
and(
gt(pointerCodesize, end), // Within bounds.
iszero(gt(start, end)) // Valid range.
)
) {
// Store the function selector of `ReadOutOfBounds()`.
mstore(0x00, 0x84eb0dd1)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
let size := sub(end, start)
// Get the pointer to the free memory and allocate
// enough 32-byte words for the data and the length of the data,
// then copy the code to the allocated memory.
// Masking with 0xffe0 will suffice, since contract size is less than 16 bits.
data := mload(0x40)
mstore(0x40, add(data, and(add(size, 0x3f), 0xffe0)))
mstore(data, size)
mstore(add(add(data, 0x20), size), 0) // Zeroize the last slot.
extcodecopy(pointer, add(data, 0x20), add(start, DATA_OFFSET), size)
}
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.17;
/// @title Version interface
/// @notice Defines contract version and type
interface IVersion {
/// @notice Contract version
function version() external view returns (uint256);
/// @notice Contract type
function contractType() external view returns (bytes32);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: BUSL-1.1
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.17;
import {ZeroAddressException} from "../interfaces/IExceptions.sol";
/// @title Sanity check trait
abstract contract SanityCheckTrait {
/// @dev Ensures that passed address is non-zero
modifier nonZeroAddress(address addr) {
_revertIfZeroAddress(addr);
_;
}
/// @dev Reverts if address is zero
function _revertIfZeroAddress(address addr) private pure {
if (addr == address(0)) revert ZeroAddressException();
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/EIP712.sol)
pragma solidity ^0.8.8;
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import {ShortStrings, ShortString} from "@openzeppelin/contracts/utils/ShortStrings.sol";
import {IERC5267} from "@openzeppelin/contracts/interfaces/IERC5267.sol";
/**
* @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
*
* The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
* thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
* they need in their contracts using a combination of `abi.encode` and `keccak256`.
*
* This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
* scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
* ({_hashTypedDataV4}).
*
* The implementation of the domain separator was designed to be as efficient as possible while still properly updating
* the chain id to protect against replay attacks on an eventual fork of the chain.
*
* NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
* https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
*
* NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain
* separator of the implementation contract. This will cause the `_domainSeparatorV4` function to always rebuild the
* separator from the immutable values, which is cheaper than accessing a cached version in cold storage.
*
* _Available since v3.4._
*
* @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
*/
abstract contract EIP712Mainnet is IERC5267 {
using ShortStrings for *;
bytes32 private constant _TYPE_HASH =
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
bytes32 private immutable _cachedDomainSeparator;
uint256 private constant _HARDCODED_CHAIN_ID = 1;
address private immutable _cachedThis;
bytes32 private immutable _hashedName;
bytes32 private immutable _hashedVersion;
ShortString private immutable _name;
ShortString private immutable _version;
string private _nameFallback;
string private _versionFallback;
/**
* @dev Initializes the domain separator and parameter caches.
*
* The meaning of `name` and `version` is specified in
* https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
*
* - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
* - `version`: the current major version of the signing domain.
*
* NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
* contract upgrade].
*/
constructor(string memory name, string memory version) {
_name = name.toShortStringWithFallback(_nameFallback);
_version = version.toShortStringWithFallback(_versionFallback);
_hashedName = keccak256(bytes(name));
_hashedVersion = keccak256(bytes(version));
_cachedDomainSeparator = _buildDomainSeparator();
_cachedThis = address(this);
}
/**
* @dev Returns the domain separator.
*/
function _domainSeparatorV4() internal view returns (bytes32) {
if (address(this) == _cachedThis) {
return _cachedDomainSeparator;
} else {
return _buildDomainSeparator();
}
}
function _buildDomainSeparator() private view returns (bytes32) {
return keccak256(abi.encode(_TYPE_HASH, _hashedName, _hashedVersion, _HARDCODED_CHAIN_ID, address(this)));
}
/**
* @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
* function returns the hash of the fully encoded EIP712 message for this domain.
*
* This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
*
* ```solidity
* bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
* keccak256("Mail(address to,string contents)"),
* mailTo,
* keccak256(bytes(mailContents))
* )));
* address signer = ECDSA.recover(digest, signature);
* ```
*/
function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
}
/**
* @dev See {EIP-5267}.
*
* _Available since v4.9._
*/
function eip712Domain()
public
view
virtual
override
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
)
{
return (
hex"0f", // 01111
_name.toStringWithFallback(_nameFallback),
_version.toStringWithFallback(_versionFallback),
_HARDCODED_CHAIN_ID,
address(this),
bytes32(0),
new uint256[](0)
);
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2025.
pragma solidity ^0.8.23;
import {IVersion} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IVersion.sol";
import {IImmutableOwnableTrait} from "./base/IImmutableOwnableTrait.sol";
import {AuditReport, Bytecode} from "./Types.sol";
/// @title Bytecode repository interface
interface IBytecodeRepository is IVersion, IImmutableOwnableTrait {
// ------ //
// EVENTS //
// ------ //
event AddAuditor(address indexed auditor, string name);
event AddPublicDomain(bytes32 indexed domain);
event AddSystemDomain(bytes32 indexed domain);
event AllowContract(bytes32 indexed bytecodeHash, bytes32 indexed contractType, uint256 indexed version);
event AuditBytecode(bytes32 indexed bytecodeHash, address indexed auditor, string reportUrl, bytes signature);
event DeployContract(
bytes32 indexed bytecodeHash,
bytes32 indexed contractType,
uint256 indexed version,
address contractAddress,
bytes constructorParams
);
event ForbidContract(bytes32 indexed bytecodeHash, bytes32 indexed contractType, uint256 indexed version);
event ForbidInitCode(bytes32 indexed initCodeHash);
event RemoveAuditor(address indexed auditor);
event RemoveContractTypeOwner(bytes32 indexed contractType);
event SetContractTypeOwner(bytes32 indexed contractType, address indexed owner);
event SetTokenSpecificPostfix(address indexed token, bytes32 indexed postfix);
event UploadBytecode(
bytes32 indexed bytecodeHash,
bytes32 indexed contractType,
uint256 indexed version,
address author,
string source,
bytes signature
);
// ------ //
// ERRORS //
// ------ //
error AuditorIsNotApprovedException(address auditor);
error AuthorIsNotContractTypeOwnerException(bytes32 contractType, address author);
error BytecodeIsAlreadyAllowedException(bytes32 contractType, uint256 version);
error BytecodeIsAlreadySignedByAuditorException(bytes32 bytecodeHash, address auditor);
error BytecodeIsNotAllowedException(bytes32 contractType, uint256 version);
error BytecodeIsNotAuditedException(bytes32 bytecodeHash);
error BytecodeIsNotUploadedException(bytes32 bytecodeHash);
error CallerIsNotBytecodeAuthorException(address caller);
error ContractIsAlreadyDeployedException(address deployedContract);
error ContractTypeIsNotInPublicDomainException(bytes32 contractType);
error DomainIsAlreadyMarkedAsPublicException(bytes32 domain);
error DomainIsAlreadyMarkedAsSystemException(bytes32 domain);
error InitCodeIsEmptyException();
error InitCodeIsForbiddenException(bytes32 initCodeHash);
error InvalidAuditorSignatureException(address auditor);
error InvalidAuthorSignatureException(address author);
error InvalidBytecodeException(bytes32 bytecodeHash);
error InvalidContractTypeException(bytes32 contractType);
error InvalidDomainException(bytes32 domain);
error InvalidPostfixException(bytes32 postfix);
error InvalidVersionException(bytes32 contractType, uint256 version);
error VersionNotFoundException(bytes32 contractType);
// --------------- //
// EIP-712 GETTERS //
// --------------- //
function BYTECODE_TYPEHASH() external view returns (bytes32);
function AUDIT_REPORT_TYPEHASH() external view returns (bytes32);
function domainSeparatorV4() external view returns (bytes32);
function computeBytecodeHash(Bytecode calldata bytecode) external view returns (bytes32);
function computeAuditReportHash(bytes32 bytecodeHash, AuditReport calldata report)
external
view
returns (bytes32);
// ------------------- //
// DEPLOYING CONTRACTS //
// ------------------- //
function isDeployedFromRepository(address deployedContract) external view returns (bool);
function getDeployedContractBytecodeHash(address deployedContract) external view returns (bytes32);
function computeAddress(
bytes32 contractType,
uint256 version,
bytes calldata constructorParams,
bytes32 salt,
address deployer
) external view returns (address);
function deploy(bytes32 contractType, uint256 version, bytes calldata constructorParams, bytes32 salt)
external
returns (address);
// ------------------ //
// UPLOADING BYTECODE //
// ------------------ //
function getBytecode(bytes32 bytecodeHash) external view returns (Bytecode memory);
function isBytecodeUploaded(bytes32 bytecodeHash) external view returns (bool);
function uploadBytecode(Bytecode calldata bytecode) external;
// ----------------- //
// AUDITING BYTECODE //
// ----------------- //
function isBytecodeAudited(bytes32 bytecodeHash) external view returns (bool);
function getAuditReports(bytes32 bytecodeHash) external view returns (AuditReport[] memory);
function getAuditReport(bytes32 bytecodeHash, uint256 index) external view returns (AuditReport memory);
function getNumAuditReports(bytes32 bytecodeHash) external view returns (uint256);
function submitAuditReport(bytes32 bytecodeHash, AuditReport calldata auditReport) external;
// ----------------- //
// ALLOWING BYTECODE //
// ----------------- //
function getAllowedBytecodeHash(bytes32 contractType, uint256 version) external view returns (bytes32);
function getContractTypeOwner(bytes32 contractType) external view returns (address);
function allowSystemContract(bytes32 bytecodeHash) external;
function allowPublicContract(bytes32 bytecodeHash) external;
function removePublicContractType(bytes32 contractType) external;
// ------------------ //
// DOMAINS MANAGEMENT //
// ------------------ //
function isSystemDomain(bytes32 domain) external view returns (bool);
function getSystemDomains() external view returns (bytes32[] memory);
function isPublicDomain(bytes32 domain) external view returns (bool);
function getPublicDomains() external view returns (bytes32[] memory);
function addPublicDomain(bytes32 domain) external;
// ------------------- //
// AUDITORS MANAGEMENT //
// ------------------- //
function isAuditor(address auditor) external view returns (bool);
function getAuditors() external view returns (address[] memory);
function getAuditorName(address auditor) external view returns (string memory);
function addAuditor(address auditor, string calldata name) external;
function removeAuditor(address auditor) external;
// -------------------- //
// FORBIDDING INIT CODE //
// -------------------- //
function isInitCodeForbidden(bytes32 initCodeHash) external view returns (bool);
function forbidInitCode(bytes32 initCodeHash) external;
// ------------------------ //
// TOKENS WITH CUSTOM LOGIC //
// ------------------------ //
function getTokenSpecificPostfix(address token) external view returns (bytes32);
function setTokenSpecificPostfix(address token, bytes32 postfix) external;
// --------------- //
// VERSION CONTROL //
// --------------- //
function getVersions(bytes32 contractType) external view returns (uint256[] memory);
function getLatestVersion(bytes32 contractType) external view returns (uint256);
function getLatestMinorVersion(bytes32 contractType, uint256 majorVersion) external view returns (uint256);
function getLatestPatchVersion(bytes32 contractType, uint256 minorVersion) external view returns (uint256);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2025.
pragma solidity ^0.8.23;
struct AddressProviderEntry {
bytes32 key;
uint256 ver;
address value;
}
struct AuditReport {
address auditor;
string reportUrl;
bytes signature;
}
struct Bytecode {
bytes32 contractType;
uint256 version;
bytes initCode;
address author;
string source;
bytes authorSignature;
}
struct BytecodePointer {
bytes32 contractType;
uint256 version;
address[] initCodePointers;
address author;
string source;
bytes authorSignature;
}
struct Call {
address target;
bytes callData;
}
struct ConnectedPriceFeed {
address token;
address[] priceFeeds;
}
struct CrossChainCall {
uint256 chainId; // 0 means to be executed on all chains
address target;
bytes callData;
}
struct DeployParams {
bytes32 postfix;
bytes32 salt;
bytes constructorParams;
}
struct DeployResult {
address newContract;
Call[] onInstallOps;
}
struct MarketFactories {
address poolFactory;
address priceOracleFactory;
address interestRateModelFactory;
address rateKeeperFactory;
address lossPolicyFactory;
}
struct PriceFeedInfo {
string name;
uint32 stalenessPeriod;
bytes32 priceFeedType;
uint256 version;
}
struct SignedBatch {
string name;
bytes32 prevHash;
CrossChainCall[] calls;
bytes[] signatures;
}
struct SignedRecoveryModeMessage {
uint256 chainId;
bytes32 startingBatchHash;
bytes[] signatures;
}
struct Split {
bool initialized;
address[] receivers;
uint16[] proportions;
}
struct TwoAdminProposal {
bytes callData;
bool confirmedByAdmin;
bool confirmedByTreasuryProxy;
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: BUSL-1.1
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2025.
pragma solidity ^0.8.23;
uint256 constant NO_VERSION_CONTROL = 0;
// Contract types and prefixes
bytes32 constant AP_ACCOUNT_FACTORY_DEFAULT = "ACCOUNT_FACTORY::DEFAULT";
bytes32 constant AP_ACL = "ACL";
bytes32 constant AP_ADDRESS_PROVIDER = "ADDRESS_PROVIDER";
bytes32 constant AP_BOT_LIST = "BOT_LIST";
bytes32 constant AP_BYTECODE_REPOSITORY = "BYTECODE_REPOSITORY";
bytes32 constant AP_CONTRACTS_REGISTER = "CONTRACTS_REGISTER";
bytes32 constant AP_CREDIT_CONFIGURATOR = "CREDIT_CONFIGURATOR";
bytes32 constant AP_CREDIT_FACADE = "CREDIT_FACADE";
bytes32 constant AP_CREDIT_FACTORY = "CREDIT_FACTORY";
bytes32 constant AP_CREDIT_MANAGER = "CREDIT_MANAGER";
bytes32 constant AP_CROSS_CHAIN_GOVERNANCE = "CROSS_CHAIN_GOVERNANCE";
bytes32 constant AP_CROSS_CHAIN_GOVERNANCE_PROXY = "CROSS_CHAIN_GOVERNANCE_PROXY";
bytes32 constant AP_CROSS_CHAIN_MULTISIG = "CROSS_CHAIN_MULTISIG";
bytes32 constant AP_GEAR_STAKING = "GEAR_STAKING";
bytes32 constant AP_GEAR_TOKEN = "GLOBAL::GEAR_TOKEN";
bytes32 constant AP_GOVERNOR = "GOVERNOR";
bytes32 constant AP_INSTANCE_MANAGER = "INSTANCE_MANAGER";
bytes32 constant AP_INSTANCE_MANAGER_PROXY = "INSTANCE_MANAGER_PROXY";
bytes32 constant AP_INTEREST_RATE_MODEL_DEFAULT = "IRM::DEFAULT";
bytes32 constant AP_INTEREST_RATE_MODEL_FACTORY = "INTEREST_RATE_MODEL_FACTORY";
bytes32 constant AP_INTEREST_RATE_MODEL_LINEAR = "IRM::LINEAR";
bytes32 constant AP_LOSS_POLICY_ALIASED = "LOSS_POLICY::ALIASED";
bytes32 constant AP_LOSS_POLICY_DEFAULT = "LOSS_POLICY::DEFAULT";
bytes32 constant AP_LOSS_POLICY_FACTORY = "LOSS_POLICY_FACTORY";
bytes32 constant AP_MARKET_CONFIGURATOR = "MARKET_CONFIGURATOR";
bytes32 constant AP_MARKET_CONFIGURATOR_FACTORY = "MARKET_CONFIGURATOR_FACTORY";
bytes32 constant AP_MARKET_CONFIGURATOR_LEGACY = "MARKET_CONFIGURATOR::LEGACY";
bytes32 constant AP_POOL = "POOL";
bytes32 constant AP_POOL_FACTORY = "POOL_FACTORY";
bytes32 constant AP_POOL_QUOTA_KEEPER = "POOL_QUOTA_KEEPER";
bytes32 constant AP_PRICE_FEED_STORE = "PRICE_FEED_STORE";
bytes32 constant AP_PRICE_ORACLE = "PRICE_ORACLE";
bytes32 constant AP_PRICE_ORACLE_FACTORY = "PRICE_ORACLE_FACTORY";
bytes32 constant AP_RATE_KEEPER_FACTORY = "RATE_KEEPER_FACTORY";
bytes32 constant AP_RATE_KEEPER_GAUGE = "RATE_KEEPER::GAUGE";
bytes32 constant AP_RATE_KEEPER_TUMBLER = "RATE_KEEPER::TUMBLER";
bytes32 constant AP_TREASURY = "TREASURY";
bytes32 constant AP_TREASURY_PROXY = "TREASURY_PROXY";
bytes32 constant AP_TREASURY_SPLITTER = "TREASURY_SPLITTER";
bytes32 constant AP_WETH_TOKEN = "WETH_TOKEN";
bytes32 constant AP_ZERO_PRICE_FEED = "PRICE_FEED::ZERO";
// Common domains
bytes32 constant DOMAIN_ACCOUNT_FACTORY = "ACCOUNT_FACTORY";
bytes32 constant DOMAIN_ADAPTER = "ADAPTER";
bytes32 constant DOMAIN_BOT = "BOT";
bytes32 constant DOMAIN_CREDIT_MANAGER = "CREDIT_MANAGER";
bytes32 constant DOMAIN_DEGEN_NFT = "DEGEN_NFT";
bytes32 constant DOMAIN_IRM = "IRM";
bytes32 constant DOMAIN_LOSS_POLICY = "LOSS_POLICY";
bytes32 constant DOMAIN_POOL = "POOL";
bytes32 constant DOMAIN_PRICE_FEED = "PRICE_FEED";
bytes32 constant DOMAIN_RATE_KEEPER = "RATE_KEEPER";
bytes32 constant DOMAIN_ZAPPER = "ZAPPER";
// Roles
bytes32 constant ROLE_EMERGENCY_LIQUIDATOR = "EMERGENCY_LIQUIDATOR";
bytes32 constant ROLE_PAUSABLE_ADMIN = "PAUSABLE_ADMIN";
bytes32 constant ROLE_UNPAUSABLE_ADMIN = "UNPAUSABLE_ADMIN";
<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: BUSL-1.1
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2025.
pragma solidity ^0.8.23;
import {LibString} from "@solady/utils/LibString.sol";
library Domain {
using LibString for string;
using LibString for bytes32;
uint128 internal constant UNDERSCORE = 1 << 95;
function getContractType(bytes32 domain, bytes32 postfix) internal pure returns (bytes32) {
if (postfix == 0) return domain;
return string.concat(domain.fromSmallString(), "::", postfix.fromSmallString()).toSmallString();
}
function extractDomain(bytes32 contractType) internal pure returns (bytes32) {
string memory str = contractType.fromSmallString();
uint256 separatorIndex = str.indexOf("::");
// If no separator found, treat the whole type as domain
if (separatorIndex == LibString.NOT_FOUND) return str.toSmallString();
return str.slice(0, separatorIndex).toSmallString();
}
function extractPostfix(bytes32 contractType) internal pure returns (bytes32) {
string memory str = contractType.fromSmallString();
uint256 separatorIndex = str.indexOf("::");
// if no separator found, return empty postfix
if (separatorIndex == LibString.NOT_FOUND) return bytes32(0);
return str.slice(separatorIndex + 2).toSmallString();
}
function isValidContractType(bytes32 contractType) internal pure returns (bool) {
bytes32 domain = extractDomain(contractType);
if (!isValidDomain(domain)) return false;
bytes32 postfix = extractPostfix(contractType);
if (!isValidPostfix(postfix)) return false;
// avoid the "DOMAIN::" case
return contractType == getContractType(domain, postfix);
}
function isValidDomain(bytes32 domain) internal pure returns (bool) {
return domain != 0 && _isValidString(domain.fromSmallString());
}
function isValidPostfix(bytes32 postfix) internal pure returns (bool) {
return _isValidString(postfix.fromSmallString());
}
function _isValidString(string memory str) internal pure returns (bool) {
return str.is7BitASCII(LibString.ALPHANUMERIC_7_BIT_ASCII | UNDERSCORE);
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: BUSL-1.1
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2025.
pragma solidity ^0.8.23;
import {IImmutableOwnableTrait} from "../interfaces/base/IImmutableOwnableTrait.sol";
abstract contract ImmutableOwnableTrait is IImmutableOwnableTrait {
address public immutable override owner;
modifier onlyOwner() {
if (msg.sender != owner) revert CallerIsNotOwnerException(msg.sender);
_;
}
constructor(address owner_) {
owner = owner_;
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)
pragma solidity ^0.8.0;
import "./math/Math.sol";
import "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant _SYMBOLS = "0123456789abcdef";
uint8 private constant _ADDRESS_LENGTH = 20;
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toString(int256 value) internal pure returns (string memory) {
return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return keccak256(bytes(a)) == keccak256(bytes(b));
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2024.
pragma solidity ^0.8.17;
// ------- //
// GENERAL //
// ------- //
/// @notice Thrown on attempting to set an important address to zero address
error ZeroAddressException();
/// @notice Thrown when attempting to pass a zero amount to a funding-related operation
error AmountCantBeZeroException();
/// @notice Thrown on incorrect input parameter
error IncorrectParameterException();
/// @notice Thrown when balance is insufficient to perform an operation
error InsufficientBalanceException();
/// @notice Thrown if parameter is out of range
error ValueOutOfRangeException();
/// @notice Thrown when trying to send ETH to a contract that is not allowed to receive ETH directly
error ReceiveIsNotAllowedException();
/// @notice Thrown on attempting to set an EOA as an important contract in the system
error AddressIsNotContractException(address);
/// @notice Thrown on attempting to receive a token that is not a collateral token or was forbidden
error TokenNotAllowedException();
/// @notice Thrown on attempting to add a token that is already in a collateral list
error TokenAlreadyAddedException();
/// @notice Thrown when attempting to use quota-related logic for a token that is not quoted in quota keeper
error TokenIsNotQuotedException();
/// @notice Thrown on attempting to interact with an address that is not a valid target contract
error TargetContractNotAllowedException();
/// @notice Thrown if function is not implemented
error NotImplementedException();
// ------------------ //
// CONTRACTS REGISTER //
// ------------------ //
/// @notice Thrown when an address is expected to be a registered credit manager, but is not
error RegisteredCreditManagerOnlyException();
/// @notice Thrown when an address is expected to be a registered pool, but is not
error RegisteredPoolOnlyException();
// ---------------- //
// ADDRESS PROVIDER //
// ---------------- //
/// @notice Reverts if address key isn't found in address provider
error AddressNotFoundException();
// ----------------- //
// POOL, PQK, GAUGES //
// ----------------- //
/// @notice Thrown by pool-adjacent contracts when a credit manager being connected has a wrong pool address
error IncompatibleCreditManagerException();
/// @notice Thrown when attempting to set an incompatible successor staking contract
error IncompatibleSuccessorException();
/// @notice Thrown when attempting to vote in a non-approved contract
error VotingContractNotAllowedException();
/// @notice Thrown when attempting to unvote more votes than there are
error InsufficientVotesException();
/// @notice Thrown when attempting to borrow more than the second point on a two-point curve
error BorrowingMoreThanU2ForbiddenException();
/// @notice Thrown when a credit manager attempts to borrow more than its limit in the current block, or in general
error CreditManagerCantBorrowException();
/// @notice Thrown when attempting to connect a quota keeper to an incompatible pool
error IncompatiblePoolQuotaKeeperException();
/// @notice Thrown when attempting to connect a gauge to an incompatible pool quota keeper
error IncompatibleGaugeException();
/// @notice Thrown when the quota is outside of min/max bounds
error QuotaIsOutOfBoundsException();
// -------------- //
// CREDIT MANAGER //
// -------------- //
/// @notice Thrown on failing a full collateral check after multicall
error NotEnoughCollateralException();
/// @notice Thrown if an attempt to approve a collateral token to adapter's target contract fails
error AllowanceFailedException();
/// @notice Thrown on attempting to perform an action for a credit account that does not exist
error CreditAccountDoesNotExistException();
/// @notice Thrown on configurator attempting to add more than 255 collateral tokens
error TooManyTokensException();
/// @notice Thrown if more than the maximum number of tokens were enabled on a credit account
error TooManyEnabledTokensException();
/// @notice Thrown when attempting to execute a protocol interaction without active credit account set
error ActiveCreditAccountNotSetException();
/// @notice Thrown when trying to update credit account's debt more than once in the same block
error DebtUpdatedTwiceInOneBlockException();
/// @notice Thrown when trying to repay all debt while having active quotas
error DebtToZeroWithActiveQuotasException();
/// @notice Thrown when a zero-debt account attempts to update quota
error UpdateQuotaOnZeroDebtAccountException();
/// @notice Thrown when attempting to close an account with non-zero debt
error CloseAccountWithNonZeroDebtException();
/// @notice Thrown when value of funds remaining on the account after liquidation is insufficient
error InsufficientRemainingFundsException();
/// @notice Thrown when Credit Facade tries to write over a non-zero active Credit Account
error ActiveCreditAccountOverridenException();
// ------------------- //
// CREDIT CONFIGURATOR //
// ------------------- //
/// @notice Thrown on attempting to use a non-ERC20 contract or an EOA as a token
error IncorrectTokenContractException();
/// @notice Thrown if the newly set LT if zero or greater than the underlying's LT
error IncorrectLiquidationThresholdException();
/// @notice Thrown if borrowing limits are incorrect: minLimit > maxLimit or maxLimit > blockLimit
error IncorrectLimitsException();
/// @notice Thrown if the new expiration date is less than the current expiration date or current timestamp
error IncorrectExpirationDateException();
/// @notice Thrown if a contract returns a wrong credit manager or reverts when trying to retrieve it
error IncompatibleContractException();
/// @notice Thrown if attempting to forbid an adapter that is not registered in the credit manager
error AdapterIsNotRegisteredException();
/// @notice Thrown if new credit configurator's set of allowed adapters differs from the current one
error IncorrectAdaptersSetException();
/// @notice Thrown if attempting to schedule a token's LT ramping that is too short in duration
error RampDurationTooShortException();
/// @notice Thrown if attempting to set liquidation fees such that the sum of premium and fee changes
error InconsistentLiquidationFeesException();
/// @notice Thrown if attempting to set expired liquidation fees such that the sum of premium and fee changes
error InconsistentExpiredLiquidationFeesException();
// ------------- //
// CREDIT FACADE //
// ------------- //
/// @notice Thrown when attempting to perform an action that is forbidden in whitelisted mode
error ForbiddenInWhitelistedModeException();
/// @notice Thrown if credit facade is not expirable, and attempted aciton requires expirability
error NotAllowedWhenNotExpirableException();
/// @notice Thrown if a selector that doesn't match any allowed function is passed to the credit facade in a multicall
error UnknownMethodException(bytes4 selector);
/// @notice Thrown if a liquidator tries to liquidate an account with a health factor above 1
error CreditAccountNotLiquidatableException();
/// @notice Thrown if a liquidator tries to liquidate an account with loss but violates the loss policy
error CreditAccountNotLiquidatableWithLossException();
/// @notice Thrown if too much new debt was taken within a single block
error BorrowedBlockLimitException();
/// @notice Thrown if the new debt principal for a credit account falls outside of borrowing limits
error BorrowAmountOutOfLimitsException();
/// @notice Thrown if a user attempts to open an account via an expired credit facade
error NotAllowedAfterExpirationException();
/// @notice Thrown if expected balances are attempted to be set twice without performing a slippage check
error ExpectedBalancesAlreadySetException();
/// @notice Thrown if attempting to perform a slippage check when excepted balances are not set
error ExpectedBalancesNotSetException();
/// @notice Thrown if balance of at least one token is less than expected during a slippage check
error BalanceLessThanExpectedException(address token);
/// @notice Thrown when trying to perform an action that is forbidden when credit account has enabled forbidden tokens
error ForbiddenTokensException(uint256 forbiddenTokensMask);
/// @notice Thrown when forbidden token quota is increased during the multicall
error ForbiddenTokenQuotaIncreasedException(address token);
/// @notice Thrown when enabled forbidden token balance is increased during the multicall
error ForbiddenTokenBalanceIncreasedException(address token);
/// @notice Thrown when the remaining token balance is increased during the liquidation
error RemainingTokenBalanceIncreasedException(address token);
/// @notice Thrown if `botMulticall` is called by an address that is not approved by account owner or is forbidden
error NotApprovedBotException(address bot);
/// @notice Thrown when attempting to perform a multicall action with no permission for it
error NoPermissionException(uint256 permission);
/// @notice Thrown when attempting to give a bot unexpected permissions
error UnexpectedPermissionsException(uint256 permissions);
/// @notice Thrown when a custom HF parameter lower than 10000 is passed into the full collateral check
error CustomHealthFactorTooLowException();
/// @notice Thrown when submitted collateral hint is not a valid token mask
error InvalidCollateralHintException(uint256 mask);
/// @notice Thrown when trying to seize underlying token during partial liquidation
error UnderlyingIsNotLiquidatableException();
/// @notice Thrown when amount of collateral seized during partial liquidation is less than required
error SeizedLessThanRequiredException(uint256 seizedAmount);
// ------ //
// ACCESS //
// ------ //
/// @notice Thrown on attempting to call an access restricted function not as credit account owner
error CallerNotCreditAccountOwnerException();
/// @notice Thrown on attempting to call an access restricted function not as configurator
error CallerNotConfiguratorException();
/// @notice Thrown on attempting to call an access-restructed function not as account factory
error CallerNotAccountFactoryException();
/// @notice Thrown on attempting to call an access restricted function not as credit manager
error CallerNotCreditManagerException();
/// @notice Thrown on attempting to call an access restricted function not as credit facade
error CallerNotCreditFacadeException();
/// @notice Thrown on attempting to pause a contract without pausable admin rights
error CallerNotPausableAdminException();
/// @notice Thrown on attempting to unpause a contract without unpausable admin rights
error CallerNotUnpausableAdminException();
/// @notice Thrown on attempting to call an access restricted function not as gauge
error CallerNotGaugeException();
/// @notice Thrown on attempting to call an access restricted function not as quota keeper
error CallerNotPoolQuotaKeeperException();
/// @notice Thrown on attempting to call an access restricted function not as voter
error CallerNotVoterException();
/// @notice Thrown on attempting to call an access restricted function not as allowed adapter
error CallerNotAdapterException();
/// @notice Thrown on attempting to call an access restricted function not as migrator
error CallerNotMigratorException();
/// @notice Thrown when an address that is not the designated executor attempts to execute a transaction
error CallerNotExecutorException();
/// @notice Thrown on attempting to call an access restricted function not as veto admin
error CallerNotVetoAdminException();
// -------- //
// BOT LIST //
// -------- //
/// @notice Thrown when attempting to set non-zero permissions for a forbidden bot
error InvalidBotException();
/// @notice Thrown when attempting to set permissions for a bot that don't meet its requirements
error IncorrectBotPermissionsException();
/// @notice Thrown when attempting to set non-zero permissions for too many bots
error TooManyActiveBotsException();
// --------------- //
// ACCOUNT FACTORY //
// --------------- //
/// @notice Thrown when trying to deploy second master credit account for a credit manager
error MasterCreditAccountAlreadyDeployedException();
/// @notice Thrown when trying to rescue funds from a credit account that is currently in use
error CreditAccountIsInUseException();
// ------------ //
// PRICE ORACLE //
// ------------ //
/// @notice Thrown on attempting to set a token price feed to an address that is not a correct price feed
error IncorrectPriceFeedException();
/// @notice Thrown on attempting to interact with a price feed for a token not added to the price oracle
error PriceFeedDoesNotExistException();
/// @notice Thrown when trying to apply an on-demand price update to a non-updatable price feed
error PriceFeedIsNotUpdatableException();
/// @notice Thrown when price feed returns incorrect price for a token
error IncorrectPriceException();
/// @notice Thrown when token's price feed becomes stale
error StalePriceException();
<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/ShortStrings.sol)
pragma solidity ^0.8.8;
import "./StorageSlot.sol";
// | string | 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA |
// | length | 0x BB |
type ShortString is bytes32;
/**
* @dev This library provides functions to convert short memory strings
* into a `ShortString` type that can be used as an immutable variable.
*
* Strings of arbitrary length can be optimized using this library if
* they are short enough (up to 31 bytes) by packing them with their
* length (1 byte) in a single EVM word (32 bytes). Additionally, a
* fallback mechanism can be used for every other case.
*
* Usage example:
*
* ```solidity
* contract Named {
* using ShortStrings for *;
*
* ShortString private immutable _name;
* string private _nameFallback;
*
* constructor(string memory contractName) {
* _name = contractName.toShortStringWithFallback(_nameFallback);
* }
*
* function name() external view returns (string memory) {
* return _name.toStringWithFallback(_nameFallback);
* }
* }
* ```
*/
library ShortStrings {
// Used as an identifier for strings longer than 31 bytes.
bytes32 private constant _FALLBACK_SENTINEL = 0x00000000000000000000000000000000000000000000000000000000000000FF;
error StringTooLong(string str);
error InvalidShortString();
/**
* @dev Encode a string of at most 31 chars into a `ShortString`.
*
* This will trigger a `StringTooLong` error is the input string is too long.
*/
function toShortString(string memory str) internal pure returns (ShortString) {
bytes memory bstr = bytes(str);
if (bstr.length > 31) {
revert StringTooLong(str);
}
return ShortString.wrap(bytes32(uint256(bytes32(bstr)) | bstr.length));
}
/**
* @dev Decode a `ShortString` back to a "normal" string.
*/
function toString(ShortString sstr) internal pure returns (string memory) {
uint256 len = byteLength(sstr);
// using `new string(len)` would work locally but is not memory safe.
string memory str = new string(32);
/// @solidity memory-safe-assembly
assembly {
mstore(str, len)
mstore(add(str, 0x20), sstr)
}
return str;
}
/**
* @dev Return the length of a `ShortString`.
*/
function byteLength(ShortString sstr) internal pure returns (uint256) {
uint256 result = uint256(ShortString.unwrap(sstr)) & 0xFF;
if (result > 31) {
revert InvalidShortString();
}
return result;
}
/**
* @dev Encode a string into a `ShortString`, or write it to storage if it is too long.
*/
function toShortStringWithFallback(string memory value, string storage store) internal returns (ShortString) {
if (bytes(value).length < 32) {
return toShortString(value);
} else {
StorageSlot.getStringSlot(store).value = value;
return ShortString.wrap(_FALLBACK_SENTINEL);
}
}
/**
* @dev Decode a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
*/
function toStringWithFallback(ShortString value, string storage store) internal pure returns (string memory) {
if (ShortString.unwrap(value) != _FALLBACK_SENTINEL) {
return toString(value);
} else {
return store;
}
}
/**
* @dev Return the length of a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
*
* WARNING: This will return the "byte length" of the string. This may not reflect the actual length in terms of
* actual characters as the UTF-8 encoding of a single character can span over multiple bytes.
*/
function byteLengthWithFallback(ShortString value, string storage store) internal view returns (uint256) {
if (ShortString.unwrap(value) != _FALLBACK_SENTINEL) {
return byteLength(value);
} else {
return bytes(store).length;
}
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC5267.sol)
pragma solidity ^0.8.0;
interface IERC5267 {
/**
* @dev MAY be emitted to signal that the domain could have changed.
*/
event EIP712DomainChanged();
/**
* @dev returns the fields and values that describe the domain separator used by this contract for EIP-712
* signature.
*/
function eip712Domain()
external
view
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// Gearbox Protocol. Generalized leverage for DeFi protocols
// (c) Gearbox Foundation, 2025.
pragma solidity ^0.8.23;
/// @title Immutable ownable trait interface
/// @notice Interface for contracts with immutable owner functionality
interface IImmutableOwnableTrait {
error CallerIsNotOwnerException(address caller);
/// @notice Returns the immutable owner address
function owner() external view returns (address);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1, "Math: mulDiv overflow");
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
// See https://cs.stackexchange.com/q/138556/92363.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
}
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
pragma solidity ^0.8.0;
/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC1967 implementation slot:
* ```solidity
* contract ERC1967 {
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*
* _Available since v4.1 for `address`, `bool`, `bytes32`, `uint256`._
* _Available since v4.9 for `string`, `bytes`._
*/
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
struct StringSlot {
string value;
}
struct BytesSlot {
bytes value;
}
/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` with member `value` located at `slot`.
*/
function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` representation of the string storage pointer `store`.
*/
function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
/**
* @dev Returns an `BytesSlot` with member `value` located at `slot`.
*/
function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
*/
function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
}