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": {
"contracts/periphery/mintburn/sponsored-oft/SponsoredOFTSrcPeriphery.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.23;
import { Quote } from "./Structs.sol";
import { QuoteSignLib } from "./QuoteSignLib.sol";
import { ComposeMsgCodec } from "./ComposeMsgCodec.sol";
import { IOFT, IOAppCore, SendParam, MessagingFee } from "../../../interfaces/IOFT.sol";
import { AddressToBytes32 } from "../../../libraries/AddressConverters.sol";
import { MinimalLZOptions } from "../../../external/libraries/MinimalLZOptions.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
/// @notice Source chain periphery contract for users to interact with to start a sponsored or a non-sponsored flow
/// that allows custom Accross-supported flows on destination chain. Uses LayzerZero's OFT as an underlying bridge
contract SponsoredOFTSrcPeriphery is Ownable {
using AddressToBytes32 for address;
using MinimalLZOptions for bytes;
using SafeERC20 for IERC20;
bytes public constant EMPTY_OFT_COMMAND = new bytes(0);
/// @notice Token that's being sent by an OFT bridge
address public immutable TOKEN;
/// @notice OFT contract to interact with to initiate the bridge
address public immutable OFT_MESSENGER;
/// @notice Source endpoint id
uint32 public immutable SRC_EID;
/// @notice Signer public key to check the signed quote against
address public signer;
/// @notice A mapping to enforce only a single usage per quote
mapping(bytes32 => bool) public quoteNonces;
/// @notice Event with auxiliary information. To be used in concert with OftSent event to get relevant quote details
event SponsoredOFTSend(
bytes32 indexed quoteNonce,
address indexed originSender,
bytes32 indexed finalRecipient,
bytes32 destinationHandler,
uint256 quoteDeadline,
uint256 maxBpsToSponsor,
uint256 maxUserSlippageBps,
bytes32 finalToken,
bytes sig
);
/// @notice Thrown when the source eid of the ioft messenger does not match the src eid supplied
error IncorrectSrcEid();
/// @notice Thrown when the supplied token does not match the supplied ioft messenger
error TokenIOFTMismatch();
/// @notice Thrown when the signer for quote does not match `signer`
error IncorrectSignature();
/// @notice Thrown if Quote has expired
error QuoteExpired();
/// @notice Thrown if Quote nonce was already used
error NonceAlreadyUsed();
/// @notice Thrown when provided msg.value is not sufficient to cover OFT bridging fee
error InsufficientNativeFee();
constructor(address _token, address _oftMessenger, uint32 _srcEid, address _signer) Ownable(msg.sender) {
TOKEN = _token;
OFT_MESSENGER = _oftMessenger;
SRC_EID = _srcEid;
if (IOAppCore(_oftMessenger).endpoint().eid() != _srcEid) {
revert IncorrectSrcEid();
}
if (IOFT(_oftMessenger).token() != _token) {
revert TokenIOFTMismatch();
}
signer = _signer;
}
/// @notice Main entrypoint function to start the user flow
function deposit(Quote calldata quote, bytes calldata signature) external payable {
// Step 1: validate quote and mark quote nonce used
_validateQuote(quote, signature);
quoteNonces[quote.signedParams.nonce] = true;
// Step 2: build oft send params from quote
(SendParam memory sendParam, MessagingFee memory fee, address refundAddress) = _buildOftTransfer(quote);
if (fee.nativeFee > msg.value) {
revert InsufficientNativeFee();
}
// OFT doesn't refund the unused native fee portion. Instead, it expects precise fee.nativeFee to be transferred
// as msg.value, so we refund the user ourselves
uint256 nativeFeeRefund = msg.value - fee.nativeFee;
if (nativeFeeRefund > 0) {
// Adapted from "@openzeppelin/contracts/utils/Address.sol";
(bool success, ) = payable(refundAddress).call{ value: nativeFeeRefund }("");
require(success, "Unable to send value, recipient may have reverted");
}
// Step 3: pull tokens from user and apporove OFT messenger
IERC20(TOKEN).safeTransferFrom(msg.sender, address(this), quote.signedParams.amountLD);
IERC20(TOKEN).forceApprove(address(OFT_MESSENGER), quote.signedParams.amountLD);
// Step 4: send oft transfer and emit event with auxiliary data
IOFT(OFT_MESSENGER).send{ value: fee.nativeFee }(sendParam, fee, refundAddress);
emit SponsoredOFTSend(
quote.signedParams.nonce,
msg.sender,
quote.signedParams.finalRecipient,
quote.signedParams.destinationHandler,
quote.signedParams.deadline,
quote.signedParams.maxBpsToSponsor,
quote.unsignedParams.maxUserSlippageBps,
quote.signedParams.finalToken,
signature
);
}
function _buildOftTransfer(
Quote calldata quote
) internal view returns (SendParam memory, MessagingFee memory, address) {
bytes memory composeMsg = ComposeMsgCodec._encode(
quote.signedParams.nonce,
quote.signedParams.deadline,
quote.signedParams.maxBpsToSponsor,
quote.unsignedParams.maxUserSlippageBps,
quote.signedParams.finalRecipient,
quote.signedParams.finalToken,
quote.signedParams.executionMode,
quote.signedParams.actionData
);
bytes memory extraOptions = MinimalLZOptions
.newOptions()
.addExecutorLzReceiveOption(uint128(quote.signedParams.lzReceiveGasLimit), uint128(0))
.addExecutorLzComposeOption(uint16(0), uint128(quote.signedParams.lzComposeGasLimit), uint128(0));
SendParam memory sendParam = SendParam(
quote.signedParams.dstEid,
quote.signedParams.destinationHandler,
// Only support OFT sends that don't take fees in sent token. Set `minAmountLD = amountLD` to enforce this
quote.signedParams.amountLD,
quote.signedParams.amountLD,
extraOptions,
composeMsg,
// Only support empty OFT commands
EMPTY_OFT_COMMAND
);
MessagingFee memory fee = IOFT(OFT_MESSENGER).quoteSend(sendParam, false);
return (sendParam, fee, quote.unsignedParams.refundRecipient);
}
function _validateQuote(Quote calldata quote, bytes calldata signature) internal view {
if (!QuoteSignLib.isSignatureValid(signer, quote.signedParams, signature)) {
revert IncorrectSignature();
}
if (quote.signedParams.deadline < block.timestamp) {
revert QuoteExpired();
}
if (quote.signedParams.srcEid != SRC_EID) {
revert IncorrectSrcEid();
}
if (quoteNonces[quote.signedParams.nonce]) {
revert NonceAlreadyUsed();
}
}
function setSigner(address _newSigner) external onlyOwner {
signer = _newSigner;
}
}
"
},
"contracts/periphery/mintburn/sponsored-oft/Structs.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.23;
import { SendParam, MessagingFee } from "../../../interfaces/IOFT.sol";
/// @notice Execution modes for the sponsored OFT flow
enum ExecutionMode {
// Send to core and perform swap (if needed) there.
DirectToCore,
// Execute arbitrary actions (like a swap) on HyperEVM, then transfer to HyperCore
ArbitraryActionsToCore,
// Execute arbitrary actions on HyperEVM only (no HyperCore transfer)
ArbitraryActionsToEVM
}
/// @notice A structure with all the relevant information about a particular sponsored bridging flow order
struct Quote {
SignedQuoteParams signedParams;
UnsignedQuoteParams unsignedParams;
}
/// @notice Signed params of the sponsored bridging flow quote
struct SignedQuoteParams {
uint32 srcEid; // Source endpoint ID in OFT system.
// Params passed into OFT.send()
uint32 dstEid; // Destination endpoint ID in OFT system.
bytes32 destinationHandler; // `to`. Recipient address. Address of our Composer contract
uint256 amountLD; // Amount to send in local decimals.
// Signed params that go into `composeMsg`
bytes32 nonce; // quote nonce
uint256 deadline; // quote deadline
uint256 maxBpsToSponsor; // max bps (of sent amount) to sponsor for 1:1
bytes32 finalRecipient; // user address on destination
bytes32 finalToken; // final token user will receive (might be different from OFT token we're sending)
// Signed gas limits for destination-side LZ execution
uint256 lzReceiveGasLimit; // gas limit for `lzReceive` call on destination side
uint256 lzComposeGasLimit; // gas limit for `lzCompose` call on destination side
// Execution mode and action data
uint8 executionMode; // ExecutionMode: DirectToCore, ArbitraryActionsToCore, or ArbitraryActionsToEVM
bytes actionData; // Encoded action data for arbitrary execution. Empty for DirectToCore mode.
}
/// @notice Unsigned params of the sponsored bridging flow quote: user is free to choose these
struct UnsignedQuoteParams {
address refundRecipient; // recipient of extra msg.value passed into the OFT send on src chain
uint256 maxUserSlippageBps; // slippage tolerance for the swap on the destination
}
"
},
"contracts/periphery/mintburn/sponsored-oft/QuoteSignLib.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.23;
import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import { SignedQuoteParams } from "./Structs.sol";
/// @notice Lib to check the signature for `SignedQuoteParams`.
/// The signature is checked against a keccak hash of abi-encoded fields of `SignedQuoteParams`
library QuoteSignLib {
using ECDSA for bytes32;
/// @notice Compute the keccak of all `SignedQuoteParams` fields
function hash(SignedQuoteParams calldata p) internal pure returns (bytes32) {
return
keccak256(
abi.encode(
p.srcEid,
p.dstEid,
p.destinationHandler,
p.amountLD,
p.nonce,
p.deadline,
p.maxBpsToSponsor,
p.finalRecipient,
p.finalToken,
p.lzReceiveGasLimit,
p.lzComposeGasLimit,
p.executionMode,
keccak256(p.actionData) // Hash the actionData to keep signature size reasonable
)
);
}
/// @notice Recover the signer for the given params and signature.
function recoverSigner(SignedQuoteParams calldata p, bytes calldata signature) internal pure returns (address) {
bytes32 digest = hash(p);
return digest.recover(signature);
}
/// @notice Verify that `expectedSigner` signed `p` with `signature`.
function isSignatureValid(
address expectedSigner,
SignedQuoteParams calldata p,
bytes calldata signature
) internal pure returns (bool) {
return recoverSigner(p, signature) == expectedSigner;
}
}
"
},
"contracts/periphery/mintburn/sponsored-oft/ComposeMsgCodec.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.23;
import { BytesLib } from "../../../external/libraries/BytesLib.sol";
/// @notice Codec for params passed in OFT `composeMsg`.
library ComposeMsgCodec {
uint256 internal constant NONCE_OFFSET = 0;
uint256 internal constant DEADLINE_OFFSET = 32;
uint256 internal constant MAX_BPS_TO_SPONSOR_OFFSET = 64;
uint256 internal constant MAX_USER_SLIPPAGE_BPS_OFFSET = 96;
uint256 internal constant FINAL_RECIPIENT_OFFSET = 128;
uint256 internal constant FINAL_TOKEN_OFFSET = 160;
uint256 internal constant EXECUTION_MODE_OFFSET = 192;
// Minimum length with empty actionData: 7 regular params (32 bytes each) and 1 dynamic byte array (minumum 64 bytes)
uint256 internal constant MIN_COMPOSE_MSG_BYTE_LENGTH = 288;
function _encode(
bytes32 nonce,
uint256 deadline,
uint256 maxBpsToSponsor,
uint256 maxUserSlippageBps,
bytes32 finalRecipient,
bytes32 finalToken,
uint8 executionMode,
bytes memory actionData
) internal pure returns (bytes memory) {
return
abi.encode(
nonce,
deadline,
maxBpsToSponsor,
maxUserSlippageBps,
finalRecipient,
finalToken,
executionMode,
actionData
);
}
function _getNonce(bytes memory data) internal pure returns (bytes32 v) {
return BytesLib.toBytes32(data, NONCE_OFFSET);
}
function _getDeadline(bytes memory data) internal pure returns (uint256 v) {
return BytesLib.toUint256(data, DEADLINE_OFFSET);
}
function _getMaxBpsToSponsor(bytes memory data) internal pure returns (uint256 v) {
return BytesLib.toUint256(data, MAX_BPS_TO_SPONSOR_OFFSET);
}
function _getMaxUserSlippageBps(bytes memory data) internal pure returns (uint256 v) {
return BytesLib.toUint256(data, MAX_USER_SLIPPAGE_BPS_OFFSET);
}
function _getFinalRecipient(bytes memory data) internal pure returns (bytes32 v) {
return BytesLib.toBytes32(data, FINAL_RECIPIENT_OFFSET);
}
function _getFinalToken(bytes memory data) internal pure returns (bytes32 v) {
return BytesLib.toBytes32(data, FINAL_TOKEN_OFFSET);
}
function _getExecutionMode(bytes memory data) internal pure returns (uint8 v) {
(, , , , , , uint8 executionMode, ) = abi.decode(
data,
(bytes32, uint256, uint256, uint256, bytes32, bytes32, uint8, bytes)
);
return executionMode;
}
function _getActionData(bytes memory data) internal pure returns (bytes memory v) {
(, , , , , , , bytes memory actionData) = abi.decode(
data,
(bytes32, uint256, uint256, uint256, bytes32, bytes32, uint8, bytes)
);
return actionData;
}
function _isValidComposeMsgBytelength(bytes memory data) internal pure returns (bool valid) {
// Message must be at least the minimum length (can be longer due to variable actionData)
valid = data.length >= MIN_COMPOSE_MSG_BYTE_LENGTH;
}
}
"
},
"contracts/interfaces/IOFT.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;
/**
* @notice This file contains minimal copies of relevant structs / interfaces for OFT bridging. Source code link:
* https://github.com/LayerZero-Labs/LayerZero-v2/blob/9a4049ae3a374e1c0ef01ac9fb53dd83f4257a68/packages/layerzero-v2/evm/oapp/contracts/oft/interfaces/IOFT.sol
* It's also published as a part of an npm package: @layerzerolabs/oft-evm. The published code is incompatible with
* our compiler version requirements, so we copy it here instead
*/
struct MessagingReceipt {
bytes32 guid;
uint64 nonce;
MessagingFee fee;
}
struct MessagingFee {
uint256 nativeFee;
uint256 lzTokenFee;
}
/**
* @dev Struct representing token parameters for the OFT send() operation.
*/
struct SendParam {
uint32 dstEid; // Destination endpoint ID.
bytes32 to; // Recipient address.
uint256 amountLD; // Amount to send in local decimals.
uint256 minAmountLD; // Minimum amount to send in local decimals.
bytes extraOptions; // Additional options supplied by the caller to be used in the LayerZero message.
bytes composeMsg; // The composed message for the send() operation.
bytes oftCmd; // The OFT command to be executed, unused in default OFT implementations.
}
/**
* @dev Struct representing OFT receipt information.
*/
struct OFTReceipt {
uint256 amountSentLD; // Amount of tokens ACTUALLY debited from the sender in local decimals.
// @dev In non-default implementations, the amountReceivedLD COULD differ from this value.
uint256 amountReceivedLD; // Amount of tokens to be received on the remote side.
}
/**
* @title IOFT
* @dev Interface for the OftChain (OFT) token.
* @dev Does not inherit ERC20 to accommodate usage by OFTAdapter as well.
* @dev This specific interface ID is '0x02e49c2c'.
*/
interface IOFT {
/**
* @notice Retrieves the address of the token associated with the OFT.
* @return token The address of the ERC20 token implementation.
*/
function token() external view returns (address);
/**
* @notice Provides a quote for the send() operation.
* @param _sendParam The parameters for the send() operation.
* @param _payInLzToken Flag indicating whether the caller is paying in the LZ token.
* @return fee The calculated LayerZero messaging fee from the send() operation.
*
* @dev MessagingFee: LayerZero msg fee
* - nativeFee: The native fee.
* - lzTokenFee: The lzToken fee.
*/
function quoteSend(SendParam calldata _sendParam, bool _payInLzToken) external view returns (MessagingFee memory);
/**
* @notice Executes the send() operation.
* @param _sendParam The parameters for the send operation.
* @param _fee The fee information supplied by the caller.
* - nativeFee: The native fee.
* - lzTokenFee: The lzToken fee.
* @param _refundAddress The address to receive any excess funds from fees etc. on the src.
* @return receipt The LayerZero messaging receipt from the send() operation.
* @return oftReceipt The OFT receipt information.
*
* @dev MessagingReceipt: LayerZero msg receipt
* - guid: The unique identifier for the sent message.
* - nonce: The nonce of the sent message.
* - fee: The LayerZero fee incurred for the message.
*/
function send(
SendParam calldata _sendParam,
MessagingFee calldata _fee,
address _refundAddress
) external payable returns (MessagingReceipt memory, OFTReceipt memory);
}
interface IEndpoint {
function eid() external view returns (uint32);
}
interface IOAppCore {
/**
* @notice Retrieves the LayerZero endpoint associated with the OApp.
* @return iEndpoint The LayerZero endpoint as an interface.
*/
function endpoint() external view returns (IEndpoint iEndpoint);
}
"
},
"contracts/libraries/AddressConverters.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;
library Bytes32ToAddress {
/**************************************
* ERRORS *
**************************************/
error InvalidBytes32();
function toAddress(bytes32 _bytes32) internal pure returns (address) {
checkAddress(_bytes32);
return address(uint160(uint256(_bytes32)));
}
function toAddressUnchecked(bytes32 _bytes32) internal pure returns (address) {
return address(uint160(uint256(_bytes32)));
}
function checkAddress(bytes32 _bytes32) internal pure {
if (!isValidAddress(_bytes32)) {
revert InvalidBytes32();
}
}
function isValidAddress(bytes32 _bytes32) internal pure returns (bool) {
return uint256(_bytes32) >> 160 == 0;
}
}
library AddressToBytes32 {
function toBytes32(address _address) internal pure returns (bytes32) {
return bytes32(uint256(uint160(_address)));
}
}
"
},
"contracts/external/libraries/MinimalLZOptions.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
import { BytesLib } from "../../external/libraries/BytesLib.sol";
import { SafeCast } from "@openzeppelin/contracts-v4/utils/math/SafeCast.sol";
/**
* @title MinimalExecutorOptions
* @notice This library is used to provide minimal required functionality of
* https://github.com/LayerZero-Labs/LayerZero-v2/blob/2ff4988f85b5c94032eb71bbc4073e69c078179d/packages/layerzero-v2/evm/messagelib/contracts/libs/ExecutorOptions.sol#L7
* Code was copied, was not modified
*/
library MinimalExecutorOptions {
uint8 internal constant WORKER_ID = 1;
uint8 internal constant OPTION_TYPE_LZRECEIVE = 1;
uint8 internal constant OPTION_TYPE_LZCOMPOSE = 3;
function encodeLzReceiveOption(uint128 _gas, uint128 _value) internal pure returns (bytes memory) {
return _value == 0 ? abi.encodePacked(_gas) : abi.encodePacked(_gas, _value);
}
function encodeLzComposeOption(uint16 _index, uint128 _gas, uint128 _value) internal pure returns (bytes memory) {
return _value == 0 ? abi.encodePacked(_index, _gas) : abi.encodePacked(_index, _gas, _value);
}
}
/**
* @title MinimalLZOptions
* @notice This library is used to provide minimal functionality of
* https://github.com/LayerZero-Labs/devtools/blob/52ad590ab249f660f803ae3aafcbf7115733359c/packages/oapp-evm/contracts/oapp/libs/OptionsBuilder.sol
* Code was copied, was not modified
*/
library MinimalLZOptions {
// @dev Only used in `onlyType3` modifier
using BytesLib for bytes;
// @dev Only used in `addExecutorOption`
using SafeCast for uint256;
// Constants for options types
uint16 internal constant TYPE_1 = 1; // legacy options type 1
uint16 internal constant TYPE_2 = 2; // legacy options type 2
uint16 internal constant TYPE_3 = 3;
// Custom error message
error InvalidSize(uint256 max, uint256 actual);
error InvalidOptionType(uint16 optionType);
// Modifier to ensure only options of type 3 are used
modifier onlyType3(bytes memory _options) {
if (_options.toUint16(0) != TYPE_3) revert InvalidOptionType(_options.toUint16(0));
_;
}
/**
* @dev Creates a new options container with type 3.
* @return options The newly created options container.
*/
function newOptions() internal pure returns (bytes memory) {
return abi.encodePacked(TYPE_3);
}
/**
* @dev Adds an executor LZ receive option to the existing options.
* @param _options The existing options container.
* @param _gas The gasLimit used on the lzReceive() function in the OApp.
* @param _value The msg.value passed to the lzReceive() function in the OApp.
* @return options The updated options container.
*
* @dev When multiples of this option are added, they are summed by the executor
* eg. if (_gas: 200k, and _value: 1 ether) AND (_gas: 100k, _value: 0.5 ether) are sent in an option to the LayerZeroEndpoint,
* that becomes (300k, 1.5 ether) when the message is executed on the remote lzReceive() function.
*/
function addExecutorLzReceiveOption(
bytes memory _options,
uint128 _gas,
uint128 _value
) internal pure onlyType3(_options) returns (bytes memory) {
bytes memory option = MinimalExecutorOptions.encodeLzReceiveOption(_gas, _value);
return addExecutorOption(_options, MinimalExecutorOptions.OPTION_TYPE_LZRECEIVE, option);
}
/**
* @dev Adds an executor LZ compose option to the existing options.
* @param _options The existing options container.
* @param _index The index for the lzCompose() function call.
* @param _gas The gasLimit for the lzCompose() function call.
* @param _value The msg.value for the lzCompose() function call.
* @return options The updated options container.
*
* @dev When multiples of this option are added, they are summed PER index by the executor on the remote chain.
* @dev If the OApp sends N lzCompose calls on the remote, you must provide N incremented indexes starting with 0.
* ie. When your remote OApp composes (N = 3) messages, you must set this option for index 0,1,2
*/
function addExecutorLzComposeOption(
bytes memory _options,
uint16 _index,
uint128 _gas,
uint128 _value
) internal pure onlyType3(_options) returns (bytes memory) {
bytes memory option = MinimalExecutorOptions.encodeLzComposeOption(_index, _gas, _value);
return addExecutorOption(_options, MinimalExecutorOptions.OPTION_TYPE_LZCOMPOSE, option);
}
/**
* @dev Adds an executor option to the existing options.
* @param _options The existing options container.
* @param _optionType The type of the executor option.
* @param _option The encoded data for the executor option.
* @return options The updated options container.
*/
function addExecutorOption(
bytes memory _options,
uint8 _optionType,
bytes memory _option
) internal pure onlyType3(_options) returns (bytes memory) {
return
abi.encodePacked(
_options,
MinimalExecutorOptions.WORKER_ID,
_option.length.toUint16() + 1, // +1 for optionType
_optionType,
_option
);
}
}
"
},
"node_modules/@openzeppelin/contracts/access/Ownable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../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.
*
* 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 Ownable is Context {
address private _owner;
/**
* @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.
*/
constructor(address initialOwner) {
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) {
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 {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
"
},
"node_modules/@openzeppelin/contracts/token/ERC20/IERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/IERC20.sol)
pragma solidity >=0.4.16;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
"
},
"node_modules/@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.5.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC-20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
/**
* @dev An operation with an ERC-20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
if (!_safeTransfer(token, to, value, true)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
if (!_safeTransferFrom(token, from, to, value, true)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Variant of {safeTransfer} that returns a bool instead of reverting if the operation is not successful.
*/
function trySafeTransfer(IERC20 token, address to, uint256 value) internal returns (bool) {
return _safeTransfer(token, to, value, false);
}
/**
* @dev Variant of {safeTransferFrom} that returns a bool instead of reverting if the operation is not successful.
*/
function trySafeTransferFrom(IERC20 token, address from, address to, uint256 value) internal returns (bool) {
return _safeTransferFrom(token, from, to, value, false);
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*
* NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
* only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
* set here.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
if (!_safeApprove(token, spender, value, false)) {
if (!_safeApprove(token, spender, 0, true)) revert SafeERC20FailedOperation(address(token));
if (!_safeApprove(token, spender, value, true)) revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that relies on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
safeTransfer(token, to, value);
} else if (!token.transferAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
* has no code. This can be used to implement an {ERC721}-like safe transfer that relies on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferFromAndCallRelaxed(
IERC1363 token,
address from,
address to,
uint256 value,
bytes memory data
) internal {
if (to.code.length == 0) {
safeTransferFrom(token, from, to, value);
} else if (!token.transferFromAndCall(from, to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
* Oppositely, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
* once without retrying, and relies on the returned value to be true.
*
* Reverts if the returned value is other than `true`.
*/
function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
forceApprove(token, to, value);
} else if (!token.approveAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity `token.transfer(to, value)` call, relaxing the requirement on the return value: the
* return value is optional (but if data is returned, it must not be false).
*
* @param token The token targeted by the call.
* @param to The recipient of the tokens
* @param value The amount of token to transfer
* @param bubble Behavior switch if the transfer call reverts: bubble the revert reason or return a false boolean.
*/
function _safeTransfer(IERC20 token, address to, uint256 value, bool bubble) private returns (bool success) {
bytes4 selector = IERC20.transfer.selector;
assembly ("memory-safe") {
let fmp := mload(0x40)
mstore(0x00, selector)
mstore(0x04, and(to, shr(96, not(0))))
mstore(0x24, value)
success := call(gas(), token, 0, 0x00, 0x44, 0x00, 0x20)
// if call success and return is true, all is good.
// otherwise (not success or return is not true), we need to perform further checks
if iszero(and(success, eq(mload(0x00), 1))) {
// if the call was a failure and bubble is enabled, bubble the error
if and(iszero(success), bubble) {
returndatacopy(fmp, 0x00, returndatasize())
revert(fmp, returndatasize())
}
// if the return value is not true, then the call is only successful if:
// - the token address has code
// - the returndata is empty
success := and(success, and(iszero(returndatasize()), gt(extcodesize(token), 0)))
}
mstore(0x40, fmp)
}
}
/**
* @dev Imitates a Solidity `token.transferFrom(from, to, value)` call, relaxing the requirement on the return
* value: the return value is optional (but if data is returned, it must not be false).
*
* @param token The token targeted by the call.
* @param from The sender of the tokens
* @param to The recipient of the tokens
* @param value The amount of token to transfer
* @param bubble Behavior switch if the transfer call reverts: bubble the revert reason or return a false boolean.
*/
function _safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 value,
bool bubble
) private returns (bool success) {
bytes4 selector = IERC20.transferFrom.selector;
assembly ("memory-safe") {
let fmp := mload(0x40)
mstore(0x00, selector)
mstore(0x04, and(from, shr(96, not(0))))
mstore(0x24, and(to, shr(96, not(0))))
mstore(0x44, value)
success := call(gas(), token, 0, 0x00, 0x64, 0x00, 0x20)
// if call success and return is true, all is good.
// otherwise (not success or return is not true), we need to perform further checks
if iszero(and(success, eq(mload(0x00), 1))) {
// if the call was a failure and bubble is enabled, bubble the error
if and(iszero(success), bubble) {
returndatacopy(fmp, 0x00, returndatasize())
revert(fmp, returndatasize())
}
// if the return value is not true, then the call is only successful if:
// - the token address has code
// - the returndata is empty
success := and(success, and(iszero(returndatasize()), gt(extcodesize(token), 0)))
}
mstore(0x40, fmp)
mstore(0x60, 0)
}
}
/**
* @dev Imitates a Solidity `token.approve(spender, value)` call, relaxing the requirement on the return value:
* the return value is optional (but if data is returned, it must not be false).
*
* @param token The token targeted by the call.
* @param spender The spender of the tokens
* @param value The amount of token to transfer
* @param bubble Behavior switch if the transfer call reverts: bubble the revert reason or return a false boolean.
*/
function _safeApprove(IERC20 token, address spender, uint256 value, bool bubble) private returns (bool success) {
bytes4 selector = IERC20.approve.selector;
assembly ("memory-safe") {
let fmp := mload(0x40)
mstore(0x00, selector)
mstore(0x04, and(spender, shr(96, not(0))))
mstore(0x24, value)
success := call(gas(), token, 0, 0x00, 0x44, 0x00, 0x20)
// if call success and return is true, all is good.
// otherwise (not success or return is not true), we need to perform further checks
if iszero(and(success, eq(mload(0x00), 1))) {
// if the call was a failure and bubble is enabled, bubble the error
if and(iszero(success), bubble) {
returndatacopy(fmp, 0x00, returndatasize())
revert(fmp, returndatasize())
}
// if the return value is not true, then the call is only successful if:
// - the token address has code
// - the returndata is empty
success := and(success, and(iszero(returndatasize()), gt(extcodesize(token), 0)))
}
mstore(0x40, fmp)
}
}
}
"
},
"node_modules/@openzeppelin/contracts/utils/cryptography/ECDSA.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.5.0) (utils/cryptography/ECDSA.sol)
pragma solidity ^0.8.20;
/**
* @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
}
/**
* @dev The signature derives the `address(0)`.
*/
error ECDSAInvalidSignature();
/**
* @dev The signature has an invalid length.
*/
error ECDSAInvalidSignatureLength(uint256 length);
/**
* @dev The signature has an S value that is in the upper half order.
*/
error ECDSAInvalidSignatureS(bytes32 s);
/**
* @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not
* return address(0) without also returning an error description. Errors are documented using an enum (error type)
* and a bytes32 providing additional information about the error.
*
* If no error is returned, then the address can be used for verification purposes.
*
* The `ecrecover` EVM precompile 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.
*
* NOTE: This function only supports 65-byte signatures. ERC-2098 short signatures are rejected. This restriction
* is DEPRECATED and will be removed in v6.0. Developers SHOULD NOT use signatures as unique identifiers; use hash
* invalidation or nonces for replay protection.
*
* 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 {MessageHashUtils-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]
*/
function tryRecover(
bytes32 hash,
bytes memory signature
) internal pure returns (address recovered, RecoverError err, bytes32 errArg) {
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.
assembly ("memory-safe") {
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, bytes32(signature.length));
}
}
/**
* @dev Variant of {tryRecover} that takes a signature in calldata
*/
function tryRecoverCalldata(
bytes32 hash,
bytes calldata signature
) internal pure returns (address recovered, RecoverError err, bytes32 errArg) {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, calldata slices would work here, but are
// significantly more expensive (length check) than using calldataload in assembly.
assembly ("memory-safe") {
r := calldataload(signature.offset)
s := calldataload(add(signature.offset, 0x20))
v := byte(0, calldataload(add(signature.offset, 0x40)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM precompile 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.
*
* NOTE: This function only supports 65-byte signatures. ERC-2098 short signatures are rejected. This restriction
* is DEPRECATED and will be removed in v6.0. Developers SHOULD NOT use signatures as unique identifiers; use hash
* invalidation or nonces for replay protection.
*
* 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 {MessageHashUtils-toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Variant of {recover} that takes a signature in calldata
*/
function recoverCalldata(bytes32 hash, bytes calldata signature) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecoverCalldata(hash, signature);
_throwError(error, errorArg);
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[ERC-2098 short signatures]
*/
function tryRecover(
bytes32 hash,
bytes32 r,
bytes32 vs
) internal pure returns (address recovered, RecoverError err, bytes32 errArg) {
unchecked {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
// We do not check for an overflow here since the shift operation results in 0 or 1.
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.
*/
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function tryRecover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address recovered, RecoverError err, bytes32 errArg) {
// 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, s);
}
// 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, bytes32(0));
}
return (signer, RecoverError.NoError, bytes32(0));
}
/**
* @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, bytes32 errorArg) = tryRecover(hash, v, r, s);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Parse a signature into its `v`, `r` and `s` components. Supports 65-byte and 64-byte (ERC-2098)
* formats. Returns (0,0,0) for invalid signatures.
*
* For 64-byte signatures, `v` is automatically normalized to 27 or 28.
* For 65-byte signatures, `v` is returned as-is and MUST already be 27 or 28 for use with ecrecover.
*
* Consider validating the result before use, or use {tryRecover}/{recover} which perform full validation.
*/
function parse(bytes memory signature) internal pure returns (uint8 v, bytes32 r, bytes32 s) {
assembly ("memory-safe") {
// Check the signature length
switch mload(signature)
// - case 65: r,s,v signature (standard)
case 65 {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
// - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098)
case 64 {
let vs := mload(add(signature, 0x40))
r := mload(add(signature, 0x20))
s := and(vs, shr(1, not(0)))
v := add(shr(255, vs), 27)
}
default {
r := 0
s := 0
v := 0
}
}
}
/**
* @dev Variant of {parse} that takes a signature in calldata
*/
function parseCalldata(bytes calldata signature) internal pure returns (uint8 v, bytes32 r, bytes32 s) {
assembly ("memory-safe") {
// Check the signature length
switch signature.length
// - case 65: r,s,v signature (standard)
case 65 {
r := calldataload(signature.offset)
s := calldataload(add(signature.offset, 0x20))
v := byte(0, calldataload(add(signature.offset, 0x40)))
}
// - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098)
case 64 {
let vs := calldataload(add(signature.offset, 0x20))
r := calldataload(signature.offset)
s := and(vs, shr(1, not(0)))
v := add(shr(255, vs), 27)
}
default {
r := 0
s := 0
v := 0
}
}
}
/**
* @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
*/
function _throwError(RecoverError error, bytes32 errorArg) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert ECDSAInvalidSignature();
} else if (error == RecoverError.InvalidSignatureLength) {
revert ECDSAInvalidSignatureLength(uint256(errorArg));
} else if (error == RecoverError.InvalidSignatureS) {
revert ECDSAInvalidSignatureS(errorArg);
}
}
}
"
},
"contracts/external/libraries/BytesLib.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;
import { Bytes } from "@openzeppelin/contracts/utils/Bytes.sol";
library BytesLib {
/**************************************
* ERRORS *
**************************************/
error OutOfBounds();
/**************************************
* FUNCTIONS *
**************************************/
// The following 4 functions are copied from solidity-bytes-utils library
// https://github.com/GNSPS/solidity-bytes-utils/blob/fc502455bb2a7e26a743378df042612dd50d1eb9/contracts/BytesLib.sol#L323C5-L398C6
// Code was copied, and slightly modified to use revert instead of require
/**
* @notice Reads a uint16 from a bytes array at a given start index
* @param _bytes The bytes array to convert
* @param _start The start index of the uint16
* @return result The uint16 result
*/
function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16 result) {
if (_bytes.length < _start + 2) {
revert OutOfBounds();
}
// solhint-disable-next-line no-inline-assembly
assembly {
result := mload(add(add(_bytes, 0x2), _start))
}
}
/**
* @notice Reads a uint32 from a bytes array at a given start index
* @param _bytes The bytes array to convert
* @param _start The start index of the uint32
* @return result The uint32 result
*/
function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32 result) {
if (_bytes.length < _start + 4) {
revert OutOfBounds();
}
// solhint-disable-next-line no-inline-assembly
assembly {
result := mload(add(add(_bytes, 0x4), _start))
}
}
/**
* @notice Reads a uint256 from a bytes array at a given start index
* @param _bytes The bytes array to convert
* @param _start The start index of the uint256
* @return result The uint256 result
*/
function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256 result) {
if (_bytes.length < _start + 32) {
revert OutOfBounds();
}
// solhint-disable-next-line no-inline-assembly
assembly {
result := mload(add(add(_bytes, 0x20), _start))
}
}
/**
* @notice Reads a bytes32 from a bytes array at a given start index
* @param _bytes The bytes array to convert
* @param _start The start index of the bytes32
* @return result The bytes32 result
*/
function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32 result) {
if (_bytes.length < _start + 32) {
revert OutOfBounds();
}
// solhint-disable-next-line no-inline-assembly
assembly {
result := mload(add(add(_bytes, 0x20), _start))
}
}
/**
* @notice Reads a bytes array from a bytes array at a given start index and length
* Source: OpenZeppelin Contracts v5 (utils/Bytes.sol)
* @param _bytes The bytes array to convert
* @param _start The start index of the bytes array
* @param _end The end index of the bytes array
* @return result The bytes array result
*/
function slice(bytes memory _bytes, uint256 _start, uint256 _end) internal pure returns (bytes memory result) {
return Bytes.slice(_bytes, _start, _end);
}
}
"
},
"node_modules/@openzeppelin/contracts-v4/utils/math/SafeCast.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.
pragma solidity ^0.8.0;
/**
* @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
* checks.
*
* Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
* easily result in undesired exploitation or bugs, since developers usually
* assume that overflows raise errors. `SafeCast` restores this intuition by
* reverting the transaction when such an operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*
* Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
* all math on `uint256` and `int256` and then downcasting.
*/
library SafeCast {
/**
* @dev Returns the downcasted uint248 from uint256, reverting on
* overflow (when the input is greater than largest uint248).
*
* Counterpart to Solidity's `uint248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*
* _Available since v4.7._
*/
function toUint248(uint256 value) internal pure returns (uint248) {
require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits");
return uint248(value);
}
/**
* @dev Returns the downcasted uint240 from uint256, reverting on
* overflow (when the input is greater than largest uint240).
*
* Counterpart to Solidity's `uint240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*
* _Available since v4.7._
*/
function toUint240(uint256 value) internal pure returns (uint240) {
require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits");
return uint240(value);
}
/**
* @dev Returns the downcasted uint232 from uint256, reverting on
* overflow (when the input is greater than largest uint232).
*
* Counterpart to Solidity's `uint232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*
* _Available since v4.7._
*/
function toUint232(uint256 value) internal pure returns (uint232) {
require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits");
return uint232(value);
}
/**
* @dev Returns the downcasted uint224 from uint256, reverting on
* overflow (when the input is greater than largest uint224).
*
* Counterpart to Solidity's `uint224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*
* _Available since v4.2._
*/
function toUint224(uint256 value) internal pure returns (uint224) {
require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
Submitted on: 2025-11-07 11:40:09
Comments
Log in to comment.
No comments yet.