Description:
Multi-signature wallet contract requiring multiple confirmations for transaction execution.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"lib/btc-light-client/packages/contracts/src/BtcTxVerifier.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import "./interfaces/IBtcMirror.sol";
import "./interfaces/IBtcTxVerifier.sol";
import "./BtcProofUtils.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
//
// #
// # #
// # # #
// # # # #
// # # # # #
// # # # # # #
// # # # # # # #
// # # # # # # # #
// # # # # # # # # #
// # # # # # # # # # #
// # # # # # # # # # # #
// # # # # # #
// + # +
// ++++ ++++
// ++++++ ++++++
// +++++++++
// +++++
// +
//
// BtcVerifier implements a merkle proof that a Bitcoin payment succeeded. It
// uses BtcMirror as a source of truth for which Bitcoin block hashes are in the
// canonical chain.
contract BtcTxVerifier is OwnableUpgradeable, IBtcTxVerifier {
IBtcMirror public mirror;
/// @dev Custom errors
error InvalidOwnerAddress();
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
function initialize(address _owner, address _mirror) external initializer {
if (_owner == address(0)) revert InvalidOwnerAddress();
__Ownable_init(_owner);
mirror = IBtcMirror(_mirror);
}
function verifyPayment(
uint256 _minConfirmations,
uint256 blockNum,
BtcTxProof calldata inclusionProof,
uint256 txOutIx,
bytes32 destScriptHash,
uint256 amountSats,
bool checkOpReturn,
uint256 opReturnOutIx,
bytes32 opReturnData
) external view returns (bool) {
bytes32 blockHash = mirror.getBlockHash(blockNum);
return BtcProofUtils.validatePayment(
blockHash, inclusionProof, txOutIx, destScriptHash, amountSats, checkOpReturn, opReturnOutIx, opReturnData
);
}
function verifyInclusion(uint256 minConfirmations, uint256 blockNum, TxInclusionProof calldata inclusionProof)
external
view
returns (bool)
{
checkHeight(blockNum, minConfirmations);
bytes32 blockHash = mirror.getBlockHash(blockNum);
return BtcProofUtils.validateInclusion(blockHash, inclusionProof);
}
function checkHeight(uint256 blockNum, uint256 minConfirmations) internal view {
uint256 mirrorHeight = mirror.getLatestBlockHeight();
require(mirrorHeight >= blockNum, "Bitcoin Mirror doesn't have that block yet");
require(mirrorHeight + 1 >= minConfirmations + blockNum, "Not enough Bitcoin block confirmations");
}
// reserved storage space to allow for layout changes in the future
uint256[50] private __gap;
}
"
},
"lib/btc-light-client/packages/contracts/src/interfaces/IBtcMirror.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
/**
* @notice Tracks Bitcoin. Provides block hashes.
*/
interface IBtcMirror {
/**
* @notice Returns the Bitcoin block hash at a specific height.
*/
function getBlockHash(uint256 number) external view returns (bytes32);
/**
* @notice Returns the height of the latest block (tip of the chain).
*/
function getLatestBlockHeight() external view returns (uint256);
/**
* @notice Returns the timestamp of the lastest block, as Unix seconds.
*/
function getLatestBlockTime() external view returns (uint256);
/**
* @notice Returns latest block height and specific block hash in one call
* @dev This is optimized for LayerZero cross-chain reads to reduce call count
* @param blockNumber The specific block number to get hash for
* @return latestHeight The latest block height
* @return requestedBlockNumber The requested block number
* @return blockHash The hash of the requested block
*/
function getLatestHeightAndBlockHash(uint256 blockNumber)
external
view
returns (uint256 latestHeight, uint256 requestedBlockNumber, bytes32 blockHash);
/**
* @notice Submits a new Bitcoin chain segment (80-byte headers) s
*/
function submit_uncheck(uint256 blockHeight, bytes calldata blockHeaders, uint8 v, bytes32 r, bytes32 s) external;
/**
* @notice Challenges a previously submitted block by proving it's invalid, and submit the correct chain.
*/
function challenge(
uint256 wrong_idx,
bytes calldata wrongBlockHeaders,
uint8 v,
bytes32 r,
bytes32 s,
uint256 blockHeight,
bytes calldata blockHeaders
) external;
}
"
},
"lib/btc-light-client/packages/contracts/src/interfaces/IBtcTxVerifier.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import "./BtcTxProof.sol";
import "./IBtcMirror.sol";
/**
* @notice Verifies Bitcoin transaction proofs.
*/
interface IBtcTxVerifier {
/**
* @notice Verifies that the a transaction cleared, paying a given amount to
* a given address. Specifically, verifies a proof that the tx was
* in block N, and that block N has at least M confirmations.
*
* Also verifies that if checkOpReturn is true, the transaction has an OP_RETURN output
* with the given data, and that the output is at the given index.
*/
function verifyPayment(
uint256 minConfirmations,
uint256 blockNum,
BtcTxProof calldata inclusionProof,
uint256 txOutIx,
bytes32 destScriptHash,
uint256 amountSats,
bool checkOpReturn,
uint256 opReturnOutIx,
bytes32 opReturnData
) external view returns (bool);
/**
* @notice Returns the underlying mirror associated with this verifier.
*/
function mirror() external view returns (IBtcMirror);
/**
* @notice Verifies that the a transaction is included in a given block.
*
* @param minConfirmations The minimum number of confirmations required for the block to be considered valid.
* @param blockNum The block number to verify inclusion in.
* @param inclusionProof The proof of inclusion of the transaction in the block.
*
* @return True if the transaction is included in the block, false otherwise.
*/
function verifyInclusion(uint256 minConfirmations, uint256 blockNum, TxInclusionProof calldata inclusionProof)
external
view
returns (bool);
}
"
},
"lib/btc-light-client/packages/contracts/src/BtcProofUtils.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import "./Endian.sol";
import "openzeppelin-contracts/contracts/utils/Strings.sol";
import "./interfaces/BtcTxProof.sol";
import "./interfaces/IBtcTxVerifier.sol";
/**
* @dev A parsed (but NOT fully validated) Bitcoin transaction.
*/
struct BitcoinTx {
/**
* @dev Whether we successfully parsed this Bitcoin TX, valid version etc.
* Does NOT check signatures or whether inputs are unspent.
*/
bool validFormat;
/**
* @dev Version. Must be 1 or 2.
*/
uint32 version;
/**
* @dev Each input spends a previous UTXO.
*/
BitcoinTxIn[] inputs;
/**
* @dev Each output creates a new UTXO.
*/
BitcoinTxOut[] outputs;
/**
* @dev Locktime. Either 0 for no lock, blocks if <500k, or seconds.
*/
uint32 locktime;
}
struct BitcoinTxIn {
/**
* @dev Previous transaction.
*/
uint256 prevTxID;
/**
* @dev Specific output from that transaction.
*/
uint32 prevTxIndex;
/**
* @dev Mostly useless for tx v1, BIP68 Relative Lock Time for tx v2.
*/
uint32 seqNo;
/**
* @dev Input script length
*/
uint32 scriptLen;
/**
* @dev Input script, spending a previous UTXO. Over 32 bytes unsupported.
*/
bytes32 script;
}
struct BitcoinTxOut {
/**
* @dev TXO value, in satoshis
*/
uint64 valueSats;
/**
* @dev Output script length
*/
uint32 scriptLen;
/**
* @dev Output script. Over 32 bytes unsupported.
*/
bytes script;
}
//
// #
// # #
// # # #
// # # # #
// # # # # #
// # # # # # #
// # # # # # # #
// # # # # # # # #
// # # # # # # # # #
// # # # # # # # # # #
// # # # # # # # # # # #
// # # # # # #
// + # +
// ++++ ++++
// ++++++ ++++++
// +++++++++
// +++++
// +
//
// BtcProofUtils provides functions to prove things about Bitcoin transactions.
// Verifies merkle inclusion proofs, transaction IDs, and payment details.
library BtcProofUtils {
/**
* @dev Validates that a given payment appears under a given block hash.
*
* This verifies all of the following:
* 1. Raw transaction really does pay X satoshis to Y script hash.
* 2. Raw transaction hashes to the given transaction ID.
* 3. Transaction ID appears under transaction root (Merkle proof).
* 4. Transaction root is part of the block header.
* 5. Block header hashes to a given block hash.
*
* The caller must separately verify that the block hash is in the chain.
*
* Always returns true or reverts with a descriptive reason.
*/
function validatePayment(
bytes32 blockHash,
BtcTxProof calldata txProof,
uint256 txOutIx,
bytes32 destScriptHash,
uint256 satoshisExpected,
bool checkOpReturn,
uint256 opReturnOutIx,
bytes32 opReturnDataExpected
) internal pure returns (bool) {
// 1. Block header to block hash
require(getBlockHash(txProof.blockHeader) == blockHash, "Block hash mismatch");
// 2. and 3. Transaction ID included in block
bytes32 blockTxRoot = getBlockTxMerkleRoot(txProof.blockHeader);
bytes32 txRoot = getTxMerkleRoot(txProof.txId, txProof.txIndex, txProof.txMerkleProof);
require(blockTxRoot == txRoot, "Tx merkle root mismatch");
// 4. Raw transaction to TxID
require(getTxID(txProof.rawTx) == txProof.txId, "Tx ID mismatch");
// 5. Finally, validate raw transaction pays stated recipient.
BitcoinTx memory parsedTx = parseBitcoinTx(txProof.rawTx);
BitcoinTxOut memory txo = parsedTx.outputs[txOutIx];
bytes32 scriptHash = sha256(txo.script);
if (destScriptHash != scriptHash) {
revert(
string(
abi.encodePacked(
"ScriptHashMismatch, destScriptHash: ",
Strings.toHexString(uint256(destScriptHash), 32),
", scriptHash: ",
Strings.toHexString(uint256(scriptHash), 32)
)
)
);
}
require(txo.valueSats == satoshisExpected, "Amount mismatch");
// 6. Check OP_RETURN output if requested
if (checkOpReturn) {
BitcoinTxOut memory opReturnTxo = parsedTx.outputs[opReturnOutIx];
bytes32 opReturnData = getOpReturnScriptData(opReturnTxo.scriptLen, opReturnTxo.script);
require(opReturnData == opReturnDataExpected, "OP_RETURN data mismatch");
}
// We've verified that blockHash contains a P2SH transaction
// that sends at least satoshisExpected to the given hash.
return true;
}
function validateInclusion(bytes32 blockHash, TxInclusionProof calldata inclusionProof)
internal
pure
returns (bool)
{
// 1. Block header to block hash
require(getBlockHash(inclusionProof.blockHeader) == blockHash, "Block hash mismatch");
// 2. Raw transaction to TxID
bytes32 txId = getTxID(inclusionProof.rawTx);
// 2. and 3. Transaction ID included in block
bytes32 blockTxRoot = getBlockTxMerkleRoot(inclusionProof.blockHeader);
bytes32 txRoot = getTxMerkleRoot(txId, inclusionProof.txIndex, inclusionProof.txMerkleProof);
require(blockTxRoot == txRoot, "Tx merkle root mismatch");
// 4. We've verified that blockHash contains the transaction
return true;
}
/**
* @dev Compute a block hash given a block header.
*/
function getBlockHash(bytes calldata blockHeader) public pure returns (bytes32) {
require(blockHeader.length == 80, "Invalid block header length");
bytes32 ret = sha256(abi.encodePacked(sha256(blockHeader)));
return bytes32(Endian.reverse256(uint256(ret)));
}
/**
* @dev Get the transactions merkle root given a block header.
*/
function getBlockTxMerkleRoot(bytes calldata blockHeader) public pure returns (bytes32) {
require(blockHeader.length == 80);
return bytes32(blockHeader[36:68]);
}
/**
* @dev Recomputes the transactions root given a merkle proof.
*/
function getTxMerkleRoot(bytes32 txId, uint256 txIndex, bytes calldata siblings) public pure returns (bytes32) {
bytes32 ret = bytes32(Endian.reverse256(uint256(txId)));
uint256 len = siblings.length / 32;
for (uint256 i = 0; i < len; i++) {
bytes32 s = bytes32(Endian.reverse256(uint256(bytes32(siblings[i * 32:(i + 1) * 32]))));
if (txIndex & 1 == 0) {
ret = doubleSha(abi.encodePacked(ret, s));
} else {
ret = doubleSha(abi.encodePacked(s, ret));
}
txIndex = txIndex >> 1;
}
return ret;
}
/**
* @dev Computes the ubiquitious Bitcoin SHA256(SHA256(x))
*/
function doubleSha(bytes memory buf) internal pure returns (bytes32) {
return sha256(abi.encodePacked(sha256(buf)));
}
/**
* @dev Recomputes the transaction ID for a raw transaction.
*/
function getTxID(bytes calldata rawTransaction) public pure returns (bytes32) {
bytes32 ret = doubleSha(rawTransaction);
return bytes32(Endian.reverse256(uint256(ret)));
}
/**
* @dev Parses a HASH-SERIALIZED Bitcoin transaction.
* This means no flags and no segwit witnesses.
*/
function parseBitcoinTx(bytes calldata rawTx) public pure returns (BitcoinTx memory ret) {
ret.version = Endian.reverse32(uint32(bytes4(rawTx[0:4])));
if (ret.version < 1 || ret.version > 2) {
return ret; // invalid version
}
// Read transaction inputs
uint256 offset = 4;
uint256 nInputs;
(nInputs, offset) = readVarInt(rawTx, offset);
ret.inputs = new BitcoinTxIn[](nInputs);
for (uint256 i = 0; i < nInputs; i++) {
BitcoinTxIn memory txIn;
txIn.prevTxID = Endian.reverse256(uint256(bytes32(rawTx[offset:offset + 32])));
offset += 32;
txIn.prevTxIndex = Endian.reverse32(uint32(bytes4(rawTx[offset:offset + 4])));
offset += 4;
uint256 nInScriptBytes;
(nInScriptBytes, offset) = readVarInt(rawTx, offset);
// require(nInScriptBytes <= 34, "Scripts over 34 bytes unsupported");
txIn.scriptLen = uint32(nInScriptBytes);
// txIn.script = bytes32(rawTx[offset:offset + nInScriptBytes]);
offset += nInScriptBytes;
txIn.seqNo = Endian.reverse32(uint32(bytes4(rawTx[offset:offset + 4])));
offset += 4;
ret.inputs[i] = txIn;
}
// Read transaction outputs
uint256 nOutputs;
(nOutputs, offset) = readVarInt(rawTx, offset);
ret.outputs = new BitcoinTxOut[](nOutputs);
for (uint256 i = 0; i < nOutputs; i++) {
BitcoinTxOut memory txOut;
txOut.valueSats = Endian.reverse64(uint64(bytes8(rawTx[offset:offset + 8])));
offset += 8;
uint256 nOutScriptBytes;
(nOutScriptBytes, offset) = readVarInt(rawTx, offset);
require(nOutScriptBytes <= 34, "Scripts over 34 bytes unsupported");
txOut.scriptLen = uint32(nOutScriptBytes);
txOut.script = rawTx[offset:offset + nOutScriptBytes];
offset += nOutScriptBytes;
ret.outputs[i] = txOut;
}
// Finally, read locktime, the last four bytes in the tx.
ret.locktime = Endian.reverse32(uint32(bytes4(rawTx[offset:offset + 4])));
offset += 4;
if (offset != rawTx.length) {
return ret; // Extra data at end of transaction.
}
// Parsing complete, sanity checks passed, return success.
ret.validFormat = true;
return ret;
}
/**
* Reads a Bitcoin-serialized varint = a u256 serialized in 1-9 bytes.
*/
function readVarInt(bytes calldata buf, uint256 offset) public pure returns (uint256 val, uint256 newOffset) {
uint8 pivot = uint8(buf[offset]);
if (pivot < 0xfd) {
val = pivot;
newOffset = offset + 1;
} else if (pivot == 0xfd) {
val = Endian.reverse16(uint16(bytes2(buf[offset + 1:offset + 3])));
newOffset = offset + 3;
} else if (pivot == 0xfe) {
val = Endian.reverse32(uint32(bytes4(buf[offset + 1:offset + 5])));
newOffset = offset + 5;
} else {
// pivot == 0xff
val = Endian.reverse64(uint64(bytes8(buf[offset + 1:offset + 9])));
newOffset = offset + 9;
}
}
/**
* @dev Verifies that `script` is a standard OP_RETURN script, and extracts the data.
* @return result The data, or 0 if verification failed.
*/
function getOpReturnScriptData(uint256 scriptLen, bytes memory script) internal pure returns (bytes32 result) {
if (scriptLen != 34) {
return 0;
}
// index 0: OP_RETURN
// index 1: OP_PUSHBYTES_32
if (script[0] != 0x6a || script[1] != 0x20) {
return 0;
}
assembly {
result := mload(add(add(script, 32), 2))
}
}
}
"
},
"lib/openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.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.
*
* The initial owner is set to the address provided by the deployer. 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 OwnableUpgradeable is Initializable, ContextUpgradeable {
/// @custom:storage-location erc7201:openzeppelin.storage.Ownable
struct OwnableStorage {
address _owner;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant OwnableStorageLocation = 0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300;
function _getOwnableStorage() private pure returns (OwnableStorage storage $) {
assembly {
$.slot := OwnableStorageLocation
}
}
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
function __Ownable_init(address initialOwner) internal onlyInitializing {
__Ownable_init_unchained(initialOwner);
}
function __Ownable_init_unchained(address initialOwner) internal onlyInitializing {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @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) {
OwnableStorage storage $ = _getOwnableStorage();
return $._owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @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 {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
OwnableStorage storage $ = _getOwnableStorage();
address oldOwner = $._owner;
$._owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
"
},
"lib/btc-light-client/packages/contracts/src/interfaces/BtcTxProof.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
/**
* @notice Proof that a transaction (rawTx) is in a given block.
*/
struct BtcTxProof {
/**
* @notice 80-byte block header.
*/
bytes blockHeader;
/**
* @notice Bitcoin transaction ID, equal to SHA256(SHA256(rawTx))
*/
// This is not gas-optimized--we could omit it and compute from rawTx. But
//s the cost is minimal, and keeping it allows better revert messages.
bytes32 txId;
/**
* @notice Index of transaction within the block.
*/
uint256 txIndex;
/**
* @notice Merkle proof. Concatenated sibling hashes, 32*n bytes.
*/
bytes txMerkleProof;
/**
* @notice Raw transaction, HASH-SERIALIZED, no witnesses.
*/
bytes rawTx;
}
/**
* @notice Proof that a transaction (rawTx) is in a given block.
*/
struct TxInclusionProof {
/**
* @notice 80-byte block header.
*/
bytes blockHeader;
/**
* @notice Index of transaction within the block.
*/
uint256 txIndex;
/**
* @notice Merkle proof. Concatenated sibling hashes, 32*n bytes.
*/
bytes txMerkleProof;
/**
* @notice Raw transaction, HASH-SERIALIZED, no witnesses.
*/
bytes rawTx;
}
"
},
"lib/btc-light-client/packages/contracts/src/Endian.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
// Bitwise math helpers for dealing with Bitcoin block headers.
// Bitcoin block fields are little-endian. Must flip to big-endian for EVM.
library Endian {
function reverse256(uint256 input) internal pure returns (uint256 v) {
v = input;
// swap bytes
uint256 pat1 = 0xFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00;
v = ((v & pat1) >> 8) | ((v & ~pat1) << 8);
// swap 2-byte long pairs
uint256 pat2 = 0xFFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000;
v = ((v & pat2) >> 16) | ((v & ~pat2) << 16);
// swap 4-byte long pairs
uint256 pat4 = 0xFFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000;
v = ((v & pat4) >> 32) | ((v & ~pat4) << 32);
// swap 8-byte long pairs
uint256 pat8 = 0xFFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF0000000000000000;
v = ((v & pat8) >> 64) | ((v & ~pat8) << 64);
// swap 16-byte long pairs
v = (v >> 128) | (v << 128);
}
function reverse64(uint64 input) internal pure returns (uint64 v) {
v = input;
// swap bytes
v = ((v & 0xFF00FF00FF00FF00) >> 8) | ((v & 0x00FF00FF00FF00FF) << 8);
// swap 2-byte long pairs
v = ((v & 0xFFFF0000FFFF0000) >> 16) | ((v & 0x0000FFFF0000FFFF) << 16);
// swap 4-byte long pairs
v = (v >> 32) | (v << 32);
}
function reverse32(uint32 input) internal pure returns (uint32 v) {
v = input;
// swap bytes
v = ((v & 0xFF00FF00) >> 8) | ((v & 0x00FF00FF) << 8);
// swap 2-byte long pairs
v = (v >> 16) | (v << 16);
}
function reverse16(uint16 input) internal pure returns (uint16 v) {
v = input;
// swap bytes
v = (v >> 8) | (v << 8);
}
}
"
},
"lib/openzeppelin-contracts/contracts/utils/Strings.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/Strings.sol)
pragma solidity ^0.8.24;
import {Math} from "./math/Math.sol";
import {SafeCast} from "./math/SafeCast.sol";
import {SignedMath} from "./math/SignedMath.sol";
import {Bytes} from "./Bytes.sol";
/**
* @dev String operations.
*/
library Strings {
using SafeCast for *;
bytes16 private constant HEX_DIGITS = "0123456789abcdef";
uint8 private constant ADDRESS_LENGTH = 20;
uint256 private constant SPECIAL_CHARS_LOOKUP =
(1 << 0x08) | // backspace
(1 << 0x09) | // tab
(1 << 0x0a) | // newline
(1 << 0x0c) | // form feed
(1 << 0x0d) | // carriage return
(1 << 0x22) | // double quote
(1 << 0x5c); // backslash
/**
* @dev The `value` string doesn't fit in the specified `length`.
*/
error StringsInsufficientHexLength(uint256 value, uint256 length);
/**
* @dev The string being parsed contains characters that are not in scope of the given base.
*/
error StringsInvalidChar();
/**
* @dev The string being parsed is not a properly formatted address.
*/
error StringsInvalidAddressFormat();
/**
* @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;
assembly ("memory-safe") {
ptr := add(add(buffer, 0x20), length)
}
while (true) {
ptr--;
assembly ("memory-safe") {
mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toStringSigned(int256 value) internal pure returns (string memory) {
return string.concat(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) {
uint256 localValue = value;
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] = HEX_DIGITS[localValue & 0xf];
localValue >>= 4;
}
if (localValue != 0) {
revert StringsInsufficientHexLength(value, length);
}
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 Converts an `address` with fixed length of 20 bytes to its checksummed ASCII `string` hexadecimal
* representation, according to EIP-55.
*/
function toChecksumHexString(address addr) internal pure returns (string memory) {
bytes memory buffer = bytes(toHexString(addr));
// hash the hex part of buffer (skip length + 2 bytes, length 40)
uint256 hashValue;
assembly ("memory-safe") {
hashValue := shr(96, keccak256(add(buffer, 0x22), 40))
}
for (uint256 i = 41; i > 1; --i) {
// possible values for buffer[i] are 48 (0) to 57 (9) and 97 (a) to 102 (f)
if (hashValue & 0xf > 7 && uint8(buffer[i]) > 96) {
// case shift by xoring with 0x20
buffer[i] ^= 0x20;
}
hashValue >>= 4;
}
return string(buffer);
}
/**
* @dev Converts a `bytes` buffer to its ASCII `string` hexadecimal representation.
*/
function toHexString(bytes memory input) internal pure returns (string memory) {
unchecked {
bytes memory buffer = new bytes(2 * input.length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 0; i < input.length; ++i) {
uint8 v = uint8(input[i]);
buffer[2 * i + 2] = HEX_DIGITS[v >> 4];
buffer[2 * i + 3] = HEX_DIGITS[v & 0xf];
}
return string(buffer);
}
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return Bytes.equal(bytes(a), bytes(b));
}
/**
* @dev Parse a decimal string and returns the value as a `uint256`.
*
* Requirements:
* - The string must be formatted as `[0-9]*`
* - The result must fit into an `uint256` type
*/
function parseUint(string memory input) internal pure returns (uint256) {
return parseUint(input, 0, bytes(input).length);
}
/**
* @dev Variant of {parseUint-string} that parses a substring of `input` located between position `begin` (included) and
* `end` (excluded).
*
* Requirements:
* - The substring must be formatted as `[0-9]*`
* - The result must fit into an `uint256` type
*/
function parseUint(string memory input, uint256 begin, uint256 end) internal pure returns (uint256) {
(bool success, uint256 value) = tryParseUint(input, begin, end);
if (!success) revert StringsInvalidChar();
return value;
}
/**
* @dev Variant of {parseUint-string} that returns false if the parsing fails because of an invalid character.
*
* NOTE: This function will revert if the result does not fit in a `uint256`.
*/
function tryParseUint(string memory input) internal pure returns (bool success, uint256 value) {
return _tryParseUintUncheckedBounds(input, 0, bytes(input).length);
}
/**
* @dev Variant of {parseUint-string-uint256-uint256} that returns false if the parsing fails because of an invalid
* character.
*
* NOTE: This function will revert if the result does not fit in a `uint256`.
*/
function tryParseUint(
string memory input,
uint256 begin,
uint256 end
) internal pure returns (bool success, uint256 value) {
if (end > bytes(input).length || begin > end) return (false, 0);
return _tryParseUintUncheckedBounds(input, begin, end);
}
/**
* @dev Implementation of {tryParseUint-string-uint256-uint256} that does not check bounds. Caller should make sure that
* `begin <= end <= input.length`. Other inputs would result in undefined behavior.
*/
function _tryParseUintUncheckedBounds(
string memory input,
uint256 begin,
uint256 end
) private pure returns (bool success, uint256 value) {
bytes memory buffer = bytes(input);
uint256 result = 0;
for (uint256 i = begin; i < end; ++i) {
uint8 chr = _tryParseChr(bytes1(_unsafeReadBytesOffset(buffer, i)));
if (chr > 9) return (false, 0);
result *= 10;
result += chr;
}
return (true, result);
}
/**
* @dev Parse a decimal string and returns the value as a `int256`.
*
* Requirements:
* - The string must be formatted as `[-+]?[0-9]*`
* - The result must fit in an `int256` type.
*/
function parseInt(string memory input) internal pure returns (int256) {
return parseInt(input, 0, bytes(input).length);
}
/**
* @dev Variant of {parseInt-string} that parses a substring of `input` located between position `begin` (included) and
* `end` (excluded).
*
* Requirements:
* - The substring must be formatted as `[-+]?[0-9]*`
* - The result must fit in an `int256` type.
*/
function parseInt(string memory input, uint256 begin, uint256 end) internal pure returns (int256) {
(bool success, int256 value) = tryParseInt(input, begin, end);
if (!success) revert StringsInvalidChar();
return value;
}
/**
* @dev Variant of {parseInt-string} that returns false if the parsing fails because of an invalid character or if
* the result does not fit in a `int256`.
*
* NOTE: This function will revert if the absolute value of the result does not fit in a `uint256`.
*/
function tryParseInt(string memory input) internal pure returns (bool success, int256 value) {
return _tryParseIntUncheckedBounds(input, 0, bytes(input).length);
}
uint256 private constant ABS_MIN_INT256 = 2 ** 255;
/**
* @dev Variant of {parseInt-string-uint256-uint256} that returns false if the parsing fails because of an invalid
* character or if the result does not fit in a `int256`.
*
* NOTE: This function will revert if the absolute value of the result does not fit in a `uint256`.
*/
function tryParseInt(
string memory input,
uint256 begin,
uint256 end
) internal pure returns (bool success, int256 value) {
if (end > bytes(input).length || begin > end) return (false, 0);
return _tryParseIntUncheckedBounds(input, begin, end);
}
/**
* @dev Implementation of {tryParseInt-string-uint256-uint256} that does not check bounds. Caller should make sure that
* `begin <= end <= input.length`. Other inputs would result in undefined behavior.
*/
function _tryParseIntUncheckedBounds(
string memory input,
uint256 begin,
uint256 end
) private pure returns (bool success, int256 value) {
bytes memory buffer = bytes(input);
// Check presence of a negative sign.
bytes1 sign = begin == end ? bytes1(0) : bytes1(_unsafeReadBytesOffset(buffer, begin)); // don't do out-of-bound (possibly unsafe) read if sub-string is empty
bool positiveSign = sign == bytes1("+");
bool negativeSign = sign == bytes1("-");
uint256 offset = (positiveSign || negativeSign).toUint();
(bool absSuccess, uint256 absValue) = tryParseUint(input, begin + offset, end);
if (absSuccess && absValue < ABS_MIN_INT256) {
return (true, negativeSign ? -int256(absValue) : int256(absValue));
} else if (absSuccess && negativeSign && absValue == ABS_MIN_INT256) {
return (true, type(int256).min);
} else return (false, 0);
}
/**
* @dev Parse a hexadecimal string (with or without "0x" prefix), and returns the value as a `uint256`.
*
* Requirements:
* - The string must be formatted as `(0x)?[0-9a-fA-F]*`
* - The result must fit in an `uint256` type.
*/
function parseHexUint(string memory input) internal pure returns (uint256) {
return parseHexUint(input, 0, bytes(input).length);
}
/**
* @dev Variant of {parseHexUint-string} that parses a substring of `input` located between position `begin` (included) and
* `end` (excluded).
*
* Requirements:
* - The substring must be formatted as `(0x)?[0-9a-fA-F]*`
* - The result must fit in an `uint256` type.
*/
function parseHexUint(string memory input, uint256 begin, uint256 end) internal pure returns (uint256) {
(bool success, uint256 value) = tryParseHexUint(input, begin, end);
if (!success) revert StringsInvalidChar();
return value;
}
/**
* @dev Variant of {parseHexUint-string} that returns false if the parsing fails because of an invalid character.
*
* NOTE: This function will revert if the result does not fit in a `uint256`.
*/
function tryParseHexUint(string memory input) internal pure returns (bool success, uint256 value) {
return _tryParseHexUintUncheckedBounds(input, 0, bytes(input).length);
}
/**
* @dev Variant of {parseHexUint-string-uint256-uint256} that returns false if the parsing fails because of an
* invalid character.
*
* NOTE: This function will revert if the result does not fit in a `uint256`.
*/
function tryParseHexUint(
string memory input,
uint256 begin,
uint256 end
) internal pure returns (bool success, uint256 value) {
if (end > bytes(input).length || begin > end) return (false, 0);
return _tryParseHexUintUncheckedBounds(input, begin, end);
}
/**
* @dev Implementation of {tryParseHexUint-string-uint256-uint256} that does not check bounds. Caller should make sure that
* `begin <= end <= input.length`. Other inputs would result in undefined behavior.
*/
function _tryParseHexUintUncheckedBounds(
string memory input,
uint256 begin,
uint256 end
) private pure returns (bool success, uint256 value) {
bytes memory buffer = bytes(input);
// skip 0x prefix if present
bool hasPrefix = (end > begin + 1) && bytes2(_unsafeReadBytesOffset(buffer, begin)) == bytes2("0x"); // don't do out-of-bound (possibly unsafe) read if sub-string is empty
uint256 offset = hasPrefix.toUint() * 2;
uint256 result = 0;
for (uint256 i = begin + offset; i < end; ++i) {
uint8 chr = _tryParseChr(bytes1(_unsafeReadBytesOffset(buffer, i)));
if (chr > 15) return (false, 0);
result *= 16;
unchecked {
// Multiplying by 16 is equivalent to a shift of 4 bits (with additional overflow check).
// This guarantees that adding a value < 16 will not cause an overflow, hence the unchecked.
result += chr;
}
}
return (true, result);
}
/**
* @dev Parse a hexadecimal string (with or without "0x" prefix), and returns the value as an `address`.
*
* Requirements:
* - The string must be formatted as `(0x)?[0-9a-fA-F]{40}`
*/
function parseAddress(string memory input) internal pure returns (address) {
return parseAddress(input, 0, bytes(input).length);
}
/**
* @dev Variant of {parseAddress-string} that parses a substring of `input` located between position `begin` (included) and
* `end` (excluded).
*
* Requirements:
* - The substring must be formatted as `(0x)?[0-9a-fA-F]{40}`
*/
function parseAddress(string memory input, uint256 begin, uint256 end) internal pure returns (address) {
(bool success, address value) = tryParseAddress(input, begin, end);
if (!success) revert StringsInvalidAddressFormat();
return value;
}
/**
* @dev Variant of {parseAddress-string} that returns false if the parsing fails because the input is not a properly
* formatted address. See {parseAddress-string} requirements.
*/
function tryParseAddress(string memory input) internal pure returns (bool success, address value) {
return tryParseAddress(input, 0, bytes(input).length);
}
/**
* @dev Variant of {parseAddress-string-uint256-uint256} that returns false if the parsing fails because input is not a properly
* formatted address. See {parseAddress-string-uint256-uint256} requirements.
*/
function tryParseAddress(
string memory input,
uint256 begin,
uint256 end
) internal pure returns (bool success, address value) {
if (end > bytes(input).length || begin > end) return (false, address(0));
bool hasPrefix = (end > begin + 1) && bytes2(_unsafeReadBytesOffset(bytes(input), begin)) == bytes2("0x"); // don't do out-of-bound (possibly unsafe) read if sub-string is empty
uint256 expectedLength = 40 + hasPrefix.toUint() * 2;
// check that input is the correct length
if (end - begin == expectedLength) {
// length guarantees that this does not overflow, and value is at most type(uint160).max
(bool s, uint256 v) = _tryParseHexUintUncheckedBounds(input, begin, end);
return (s, address(uint160(v)));
} else {
return (false, address(0));
}
}
function _tryParseChr(bytes1 chr) private pure returns (uint8) {
uint8 value = uint8(chr);
// Try to parse `chr`:
// - Case 1: [0-9]
// - Case 2: [a-f]
// - Case 3: [A-F]
// - otherwise not supported
unchecked {
if (value > 47 && value < 58) value -= 48;
else if (value > 96 && value < 103) value -= 87;
else if (value > 64 && value < 71) value -= 55;
else return type(uint8).max;
}
return value;
}
/**
* @dev Escape special characters in JSON strings. This can be useful to prevent JSON injection in NFT metadata.
*
* WARNING: This function should only be used in double quoted JSON strings. Single quotes are not escaped.
*
* NOTE: This function escapes all unicode characters, and not just the ones in ranges defined in section 2.5 of
* RFC-4627 (U+0000 to U+001F, U+0022 and U+005C). ECMAScript's `JSON.parse` does recover escaped unicode
* characters that are not in this range, but other tooling may provide different results.
*/
function escapeJSON(string memory input) internal pure returns (string memory) {
bytes memory buffer = bytes(input);
bytes memory output = new bytes(2 * buffer.length); // worst case scenario
uint256 outputLength = 0;
for (uint256 i; i < buffer.length; ++i) {
bytes1 char = bytes1(_unsafeReadBytesOffset(buffer, i));
if (((SPECIAL_CHARS_LOOKUP & (1 << uint8(char))) != 0)) {
output[outputLength++] = "\\";
if (char == 0x08) output[outputLength++] = "b";
else if (char == 0x09) output[outputLength++] = "t";
else if (char == 0x0a) output[outputLength++] = "n";
else if (char == 0x0c) output[outputLength++] = "f";
else if (char == 0x0d) output[outputLength++] = "r";
else if (char == 0x5c) output[outputLength++] = "\\";
else if (char == 0x22) {
// solhint-disable-next-line quotes
output[outputLength++] = '"';
}
} else {
output[outputLength++] = char;
}
}
// write the actual length and deallocate unused memory
assembly ("memory-safe") {
mstore(output, outputLength)
mstore(0x40, add(output, shl(5, shr(5, add(outputLength, 63)))))
}
return string(output);
}
/**
* @dev Reads a bytes32 from a bytes array without bounds checking.
*
* NOTE: making this function internal would mean it could be used with memory unsafe offset, and marking the
* assembly block as such would prevent some optimizations.
*/
function _unsafeReadBytesOffset(bytes memory buffer, uint256 offset) private pure returns (bytes32 value) {
// This is not memory safe in the general case, but all calls to this private function are within bounds.
assembly ("memory-safe") {
value := mload(add(add(buffer, 0x20), offset))
}
}
}
"
},
"lib/openzeppelin-contracts-upgradeable/contracts/utils/ContextUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @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 ContextUpgradeable is Initializable {
function __Context_init() internal onlyInitializing {
}
function __Context_init_unchained() internal onlyInitializing {
}
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;
}
}
"
},
"lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.20;
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Storage of the initializable contract.
*
* It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
* when using with upgradeable contracts.
*
* @custom:storage-location erc7201:openzeppelin.storage.Initializable
*/
struct InitializableStorage {
/**
* @dev Indicates that the contract has been initialized.
*/
uint64 _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool _initializing;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
/**
* @dev The contract is already initialized.
*/
error InvalidInitialization();
/**
* @dev The contract is not initializing.
*/
error NotInitializing();
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint64 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
* number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
* production.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
// Cache values to avoid duplicated sloads
bool isTopLevelCall = !$._initializing;
uint64 initialized = $._initialized;
// Allowed calls:
// - initialSetup: the contract is not in the initializing state and no previous version was
// initialized
// - construction: the contract is initialized at version 1 (no reinitialization) and the
// current contract is just being deployed
bool initialSetup = initialized == 0 && isTopLevelCall;
bool construction = initialized == 1 && address(this).code.length == 0;
if (!initialSetup && !construction) {
revert InvalidInitialization();
}
$._initialized = 1;
if (isTopLevelCall) {
$._initializing = true;
}
_;
if (isTopLevelCall) {
$._initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint64 version) {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing || $._initialized >= version) {
revert InvalidInitialization();
}
$._initialized = version;
$._initializing = true;
_;
$._initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
_checkInitializing();
_;
}
/**
* @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
*/
function _checkInitializing() internal view virtual {
if (!_isInitializing()) {
revert NotInitializing();
}
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing) {
revert InvalidInitialization();
}
if ($._initialized != type(uint64).max) {
$._initialized = type(uint64).max;
emit Initialized(type(uint64).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint64) {
return _getInitializableStorage()._initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _getInitializableStorage()._initializing;
}
/**
* @dev Pointer to storage slot. Allows integrators to override it with a custom storage location.
*
* NOTE: Consider following the ERC-7201 formula to derive storage locations.
*/
function _initializableStorageSlot() internal pure virtual returns (bytes32) {
return INITIALIZABLE_STORAGE;
}
/**
* @dev Returns a pointer to the storage namespace.
*/
// solhint-disable-next-line var-name-mixedcase
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
bytes32 slot = _initializableStorageSlot();
assembly {
$.slot := slot
}
}
}
"
},
"lib/openzeppelin-contracts/contracts/utils/math/Math.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
import {Panic} from "../Panic.sol";
import {SafeCast} from "./SafeCast.sol";
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Return the 512-bit addition of two uint256.
*
* The result is stored in two 256 variables such that sum = high * 2²⁵⁶ + low.
*/
function add512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {
assembly ("memory-safe") {
low := add(a, b)
high := lt(low, a)
}
}
/**
* @dev Return the 512-bit multiplication of two uint256.
*
* The result is stored in two 256 variables such that product = high * 2²⁵⁶ + low.
*/
function mul512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {
// 512-bit multiply [high low] = x * y. Compute the product mod 2²⁵⁶ and mod 2²⁵⁶ - 1, then use
// the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = high * 2²⁵⁶ + low.
assembly ("memory-safe") {
let mm := mulmod(a, b, not(0))
low := mul(a, b)
high := sub(sub(mm, low), lt(mm, low))
}
}
/**
* @dev Returns the addition of two unsigned integers, with a success flag (no overflow).
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
uint256 c = a + b;
success = c >= a;
result = c * SafeCast.toUint(success);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with a success flag (no overflow).
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
uint256 c = a - b;
success = c <= a;
result = c * SafeCast.toUint(success);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with a success flag (no overflow).
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
uint256 c = a * b;
assembly ("memory-safe") {
// Only true when the multiplication doesn't overflow
// (c / a == b) || (a == 0)
success := or(eq(div(c, a), b), iszero(a))
}
// equivalent to: success ? c : 0
result = c * SafeCast.toUint(success);
}
}
/**
* @dev Returns the division of two unsigned integers, with a success flag (no division by zero).
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
success = b > 0;
assembly ("memory-safe") {
// The `DIV` opcode returns zero when the denominator is 0.
result := div(a, b)
}
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a success flag (no division by zero).
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
success = b > 0;
assembly ("memory-safe") {
// The `MOD` opcode returns zero when the denominator is 0.
result := mod(a, b)
}
}
}
/**
* @dev Unsigned saturating addition, bounds to `2²⁵⁶ - 1` instead of overflowing.
*/
function saturatingAdd(uint256 a, uint256 b) internal pure returns (uint256) {
(bool success, uint256 result) = tryAdd(a, b);
return ternary(success, result, type(uint256).max);
}
/**
* @dev Unsigned saturating subtraction, bounds to zero instead of overflowing.
*/
function saturatingSub(uint256 a, uint256 b) internal pure returns (uint256) {
(, uint256 result) = trySub(a, b);
return result;
}
/**
* @dev Unsigned saturating multiplication, bounds to `2²⁵⁶ - 1` instead of overflowing.
*/
function saturatingMul(uint256 a, uint256 b) internal pure returns (uint256) {
(bool success, uint256 result) = tryMul(a, b);
return ternary(success, result, type(uint256).max);
}
/**
* @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.
*
* IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.
* However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute
* one branch when needed, making this function more expensive.
*/
function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256) {
unchecked {
// branchless ternary works because:
// b ^ (a ^ b) == a
// b ^ 0 == b
return b ^ ((a ^ b) * SafeCast.toUint(condition));
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return ternary(a > b, a, b);
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return ternary(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 towa
Submitted on: 2025-10-20 14:07:41
Comments
Log in to comment.
No comments yet.