Description:
Proxy contract enabling upgradeable smart contract patterns. Delegates calls to an implementation contract.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"src/TKGasStation/TKGasStation.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
import {TKGasDelegate} from "./TKGasDelegate.sol";
import {ECDSA} from "solady/utils/ECDSA.sol";
import {EIP712} from "solady/utils/EIP712.sol";
import {IBatchExecution} from "./IBatchExecution.sol";
contract TKGasStation is EIP712 {
// Custom errors
error BatchSizeExceeded();
error DeadlineExceeded();
error InvalidOutputContract();
error InvalidNonce();
error InvalidCounter();
// EIP712 type hashes (precomputed for gas optimization)
bytes32 private constant EXECUTION_TYPEHASH = 0xc7deb0df5ad588824bf0996cb781fd274b4eee76898f919f348ecc59cc18e0e1;
// Original: keccak256("Execution(uint256 nonce,address outputContract,uint256 ethAmount,bytes arguments)")
bytes32 private constant BATCH_EXECUTION_TYPEHASH =
0xd85d05c04cabb2317dceb76fa66d4255c03f39a8feb95370f173520c44b7181f;
// Original: keccak256("BatchExecution(uint256 nonce,Execution[] executions)Execution(address outputContract,uint256 ethAmount,bytes arguments)")
bytes32 private constant BURN_NONCE_TYPEHASH = 0x4850dd989ccc5177bbe92de67c630ed29a206d9da5f1da7d2f562d1a43ee21d0;
// Original: keccak256("BurnNonce(uint256 nonce)")
bytes32 private constant TIMEBOXED_EXECUTION_TYPEHASH =
0x572542ff5f8730cc3585cab0d01b4696eadf4bd390c1dbbaa4467a76cb6f95bf;
// Original: keccak256("TimeboxedExecution(uint128 counter,uint128 deadline,address sender,address outputContract)")
bytes32 private constant ARBITRARY_TIMEBOXED_EXECUTION_TYPEHASH =
0xc0d6acc328e7656b4ab6234f5efb8bc56b83d5b67d829ae64ea7ebe07f0968ee;
// Original: keccak256("ArbitraryTimeboxedExecution(uint128 counter,uint128 deadline,address sender)")
bytes32 private constant BURN_TIMEBOXED_COUNTER_TYPEHASH =
0x96d439a73c6f9c1949a24d89d523289f8d4857543fa33be656cc2a3037807baa;
// Original: keccak256("BurnTimeboxedCounter(uint128 counter,address sender)")
// Maximum batch size to prevent griefing attacks
uint256 public constant MAX_BATCH_SIZE = 50;
TKGasDelegate public immutable TKGlobalGasDelegate; // exact delegate instance for this station
mapping(address => uint256) public nonce; //sequentional nonce for each address
mapping(address => mapping(address => uint128)) public timeboxedCounter; //timeboxed counter for each address + sender combination to enable blocking a sender
constructor() EIP712() {
TKGlobalGasDelegate = new TKGasDelegate{salt: keccak256(abi.encodePacked(address(this)))}(address(this));
}
function _domainNameAndVersion()
internal
pure
override
returns (string memory name, string memory version)
{
name = "TKGasStation";
version = "1";
}
function hashExecution(uint256 _nonce, address _outputContract, uint256 _ethAmount, bytes calldata _arguments)
external
view
returns (bytes32)
{
return _hashTypedData(
keccak256(abi.encode(EXECUTION_TYPEHASH, _nonce, _outputContract, _ethAmount, keccak256(_arguments)))
);
}
function execute(uint256 _nonce, address _outputContract, bytes calldata _arguments, bytes calldata _signature)
external
returns (bool, bytes memory)
{
bytes32 argsHash = keccak256(_arguments);
bytes32 hash;
assembly {
let ptr := mload(0x40) // Get free memory pointer
mstore(ptr, EXECUTION_TYPEHASH)
mstore(add(ptr, 0x20), _nonce)
mstore(add(ptr, 0x40), _outputContract)
mstore(add(ptr, 0x60), 0) // ethAmount = 0
mstore(add(ptr, 0x80), argsHash)
hash := keccak256(ptr, 0xa0)
}
hash = _hashTypedData(hash);
address signer = ECDSA.recover(hash, _signature);
uint256 currentNonce = nonce[signer];
if (_nonce == currentNonce) {
unchecked {
nonce[signer] = currentNonce + 1;
}
return TKGasDelegate(payable(signer)).execute(_outputContract, _arguments);
}
revert InvalidNonce();
}
function execute(
uint256 _nonce,
address _outputContract,
uint256 _ethAmount,
bytes calldata _arguments,
bytes calldata _signature
) external returns (bool, bytes memory) {
bytes32 argsHash = keccak256(_arguments);
bytes32 hash; // all this assembly to avoid using abi.encode
assembly {
let ptr := mload(0x40) // Get free memory pointer
mstore(ptr, EXECUTION_TYPEHASH)
mstore(add(ptr, 0x20), _nonce)
mstore(add(ptr, 0x40), _outputContract)
mstore(add(ptr, 0x60), _ethAmount)
mstore(add(ptr, 0x80), argsHash)
hash := keccak256(ptr, 0xa0)
}
hash = _hashTypedData(hash);
address signer = ECDSA.recover(hash, _signature);
uint256 currentNonce = nonce[signer];
if (_nonce == currentNonce) {
unchecked {
nonce[signer] = currentNonce + 1;
}
return TKGasDelegate(payable(signer)).execute(_outputContract, _ethAmount, _arguments);
}
revert InvalidNonce();
}
function hashBurnNonce(uint256 _nonce) external view returns (bytes32) {
bytes32 hash;
assembly {
let ptr := mload(0x40) // Get free memory pointer
mstore(ptr, BURN_NONCE_TYPEHASH)
mstore(add(ptr, 0x20), _nonce)
hash := keccak256(ptr, 0x40)
}
return _hashTypedData(hash);
}
function burnNonce(uint256 _nonce, bytes calldata _signature) external {
bytes32 hash;
assembly {
let ptr := mload(0x40)
mstore(ptr, BURN_NONCE_TYPEHASH)
mstore(add(ptr, 0x20), _nonce)
hash := keccak256(ptr, 0x40)
}
hash = _hashTypedData(hash);
address signer = ECDSA.recover(hash, _signature);
if (_nonce != nonce[signer]) {
revert InvalidNonce();
}
unchecked {
++nonce[signer];
}
}
function burnNonce() external {
unchecked {
++nonce[msg.sender];
}
}
/* Timeboxed execution */
function hashTimeboxedExecution(uint128 _counter, uint128 _deadline, address _sender, address _outputContract)
external
view
returns (bytes32)
{
bytes32 hash;
assembly {
let ptr := mload(0x40) // Get free memory pointer
mstore(ptr, TIMEBOXED_EXECUTION_TYPEHASH)
mstore(add(ptr, 0x20), _counter)
mstore(add(ptr, 0x40), _deadline)
mstore(add(ptr, 0x60), _sender)
mstore(add(ptr, 0x80), _outputContract)
hash := keccak256(ptr, 0xa0)
}
return _hashTypedData(hash);
}
function hashArbitraryTimeboxedExecution(uint128 _counter, uint128 _deadline, address _sender)
external
view
returns (bytes32)
{
bytes32 hash;
assembly {
let ptr := mload(0x40) // Get free memory pointer
mstore(ptr, ARBITRARY_TIMEBOXED_EXECUTION_TYPEHASH)
mstore(add(ptr, 0x20), _counter)
mstore(add(ptr, 0x40), _deadline)
mstore(add(ptr, 0x60), _sender)
hash := keccak256(ptr, 0x80)
}
return _hashTypedData(hash);
}
function hashBurnTimeboxedCounter(uint128 _counter, address _sender) external view returns (bytes32) {
bytes32 hash;
assembly {
let ptr := mload(0x40) // Get free memory pointer
mstore(ptr, BURN_TIMEBOXED_COUNTER_TYPEHASH)
mstore(add(ptr, 0x20), _counter)
mstore(add(ptr, 0x40), _sender)
hash := keccak256(ptr, 0x60)
}
return _hashTypedData(hash);
}
function executeTimeboxed(
uint128 _counter,
uint128 _deadline,
address _outputContract,
uint256 _ethAmount,
bytes calldata _arguments,
bytes calldata _signature
) external returns (bool, bytes memory) {
// Check if deadline has passed
if (block.timestamp > _deadline) {
revert DeadlineExceeded();
}
address sender = msg.sender;
bytes32 hash;
assembly {
let ptr := mload(0x40) // Get free memory pointer
mstore(ptr, TIMEBOXED_EXECUTION_TYPEHASH)
mstore(add(ptr, 0x20), _counter)
mstore(add(ptr, 0x40), _deadline)
mstore(add(ptr, 0x60), sender)
mstore(add(ptr, 0x80), _outputContract)
hash := keccak256(ptr, 0xa0)
}
hash = _hashTypedData(hash);
address signer = ECDSA.recover(hash, _signature);
if (_counter != timeboxedCounter[signer][msg.sender]) {
revert InvalidCounter();
}
return TKGasDelegate(payable(signer)).execute(_outputContract, _ethAmount, _arguments);
}
function executeTimeboxed(
uint128 _counter,
uint128 _deadline,
address _outputContract,
bytes calldata _arguments,
bytes calldata _signature
) external returns (bool, bytes memory) {
// Check if deadline has passed
if (block.timestamp > _deadline) {
revert DeadlineExceeded();
}
address sender = msg.sender;
bytes32 hash;
assembly {
let ptr := mload(0x40) // Get free memory pointer
mstore(ptr, TIMEBOXED_EXECUTION_TYPEHASH)
mstore(add(ptr, 0x20), _counter)
mstore(add(ptr, 0x40), _deadline)
mstore(add(ptr, 0x60), sender)
mstore(add(ptr, 0x80), _outputContract)
hash := keccak256(ptr, 0xa0)
}
hash = _hashTypedData(hash);
address signer = ECDSA.recover(hash, _signature);
if (_counter != timeboxedCounter[signer][msg.sender]) {
revert InvalidCounter();
}
// Execute the timeboxed transaction (counter does NOT increment for timeboxed)
return TKGasDelegate(payable(signer)).execute(_outputContract, _arguments);
}
function executeBatchTimeboxed(
uint128 _counter,
uint128 _deadline,
address _outputContract,
IBatchExecution.Execution[] calldata _executions,
bytes calldata _signature
) external returns (bool, bytes[] memory) {
// Check if deadline has passed
if (block.timestamp > _deadline) {
revert DeadlineExceeded();
}
// Prevent griefing attacks by limiting batch size
if (_executions.length > MAX_BATCH_SIZE) {
revert BatchSizeExceeded();
}
address sender = msg.sender;
bytes32 hash;
assembly {
let ptr := mload(0x40) // Get free memory pointer
mstore(ptr, TIMEBOXED_EXECUTION_TYPEHASH)
mstore(add(ptr, 0x20), _counter)
mstore(add(ptr, 0x40), _deadline)
mstore(add(ptr, 0x60), sender)
mstore(add(ptr, 0x80), _outputContract)
hash := keccak256(ptr, 0xa0)
}
hash = _hashTypedData(hash);
address signer = ECDSA.recover(hash, _signature);
if (_counter != timeboxedCounter[signer][msg.sender]) {
revert InvalidCounter();
}
for (uint256 i = 0; i < _executions.length;) {
if (_executions[i].outputContract != _outputContract) {
revert InvalidOutputContract();
}
unchecked {
++i;
}
}
// Execute the timeboxed transaction
return TKGasDelegate(payable(signer)).executeBatch(_executions);
}
function executeTimeboxedArbitrary(
uint128 _counter,
uint128 _deadline,
address _outputContract,
uint256 _ethAmount,
bytes calldata _arguments,
bytes calldata _signature
) external returns (bool, bytes memory) {
// Check if deadline has passed
if (block.timestamp > _deadline) {
revert DeadlineExceeded();
}
address sender = msg.sender;
bytes32 hash;
assembly {
let ptr := mload(0x40) // Get free memory pointer
mstore(ptr, ARBITRARY_TIMEBOXED_EXECUTION_TYPEHASH)
mstore(add(ptr, 0x20), _counter)
mstore(add(ptr, 0x40), _deadline)
mstore(add(ptr, 0x60), sender)
hash := keccak256(ptr, 0x80)
}
hash = _hashTypedData(hash);
address signer = ECDSA.recover(hash, _signature);
if (_counter != timeboxedCounter[signer][msg.sender]) {
revert InvalidCounter();
}
// Execute the timeboxed transaction
return TKGasDelegate(payable(signer)).execute(_outputContract, _ethAmount, _arguments);
}
function executeBatchTimeboxedArbitrary(
uint128 _counter,
uint128 _deadline,
IBatchExecution.Execution[] calldata _executions,
bytes calldata _signature
) external returns (bool, bytes[] memory) {
// Check if deadline has passed
if (block.timestamp > _deadline) {
revert DeadlineExceeded();
}
// Prevent griefing attacks by limiting batch size
if (_executions.length > MAX_BATCH_SIZE) {
revert BatchSizeExceeded();
}
address sender = msg.sender;
bytes32 hash;
assembly {
let ptr := mload(0x40) // Get free memory pointer
mstore(ptr, ARBITRARY_TIMEBOXED_EXECUTION_TYPEHASH)
mstore(add(ptr, 0x20), _counter)
mstore(add(ptr, 0x40), _deadline)
mstore(add(ptr, 0x60), sender)
hash := keccak256(ptr, 0x80)
}
hash = _hashTypedData(hash);
address signer = ECDSA.recover(hash, _signature);
if (_counter != timeboxedCounter[signer][msg.sender]) {
revert InvalidCounter();
}
// Execute the timeboxed transaction
return TKGasDelegate(payable(signer)).executeBatch(_executions);
}
function burnTimeboxedCounter(uint128 _counter, address _sender, bytes calldata _signature) external {
bytes32 hash;
assembly {
let ptr := mload(0x40) // Get free memory pointer
mstore(ptr, BURN_TIMEBOXED_COUNTER_TYPEHASH)
mstore(add(ptr, 0x20), _counter)
mstore(add(ptr, 0x40), _sender)
hash := keccak256(ptr, 0x60)
}
hash = _hashTypedData(hash);
address signer = ECDSA.recover(hash, _signature);
if (_counter != timeboxedCounter[signer][_sender]) {
revert InvalidCounter();
}
unchecked {
++timeboxedCounter[signer][_sender];
}
}
function burnTimeboxedCounter(address _sender) external {
unchecked {
++timeboxedCounter[msg.sender][_sender];
}
}
function hashBatchExecution(uint256 _nonce, IBatchExecution.Execution[] memory _executions)
external
view
returns (bytes32)
{
return _hashTypedData(
keccak256(abi.encode(BATCH_EXECUTION_TYPEHASH, _nonce, keccak256(abi.encode(_executions))))
);
}
function executeBatch(uint256 _nonce, IBatchExecution.Execution[] calldata _executions, bytes calldata _signature)
external
returns (bool, bytes[] memory)
{
// Prevent griefing attacks by limiting batch size
if (_executions.length > MAX_BATCH_SIZE) {
revert BatchSizeExceeded();
}
bytes32 hash = _hashTypedData(
keccak256(abi.encode(BATCH_EXECUTION_TYPEHASH, _nonce, keccak256(abi.encode(_executions))))
);
address signer = ECDSA.recover(hash, _signature);
if (_nonce != nonce[signer]) {
revert InvalidNonce();
}
unchecked {
++nonce[signer];
}
return TKGasDelegate(payable(signer)).executeBatch(_executions);
}
}
"
},
"src/TKGasStation/TKGasDelegate.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
import {IBatchExecution} from "./IBatchExecution.sol";
// Minimal interfaces defined inline to save gas
interface IERC721Receiver {
function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data)
external
pure
returns (bytes4);
}
interface IERC1155Receiver {
function onERC1155Received(address operator, address from, uint256 id, uint256 value, bytes calldata data)
external
pure
returns (bytes4);
function onERC1155BatchReceived(
address operator,
address from,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
) external pure returns (bytes4);
}
contract TKGasDelegate is IERC1155Receiver, IERC721Receiver {
// Custom errors
error ExecutionFailed();
error NotPaymaster();
address public immutable paymaster;
// note: This should not be a clonable proxy contract since it needs the state variables to be part of the immutable variables (bytecode)
constructor(address _paymaster) {
paymaster = _paymaster;
}
/* External functions */
function execute(address _outContract, bytes calldata _executionData) external returns (bool, bytes memory) {
if (msg.sender != paymaster) revert NotPaymaster();
(bool success, bytes memory result) = _outContract.call(_executionData);
if (success) {
return (success, result);
}
revert ExecutionFailed();
}
function execute(address _outContract, uint256 _ethAmount, bytes calldata _executionData)
external
returns (bool, bytes memory)
{
if (msg.sender != paymaster) revert NotPaymaster();
(bool success, bytes memory result) = _outContract.call{value: _ethAmount}(_executionData);
if (success) {
return (success, result);
}
revert ExecutionFailed();
}
function executeBatch(IBatchExecution.Execution[] calldata _executions) external returns (bool, bytes[] memory) {
if (msg.sender != paymaster) revert NotPaymaster();
uint256 length = _executions.length;
bytes[] memory results = new bytes[](length);
// Cache array access to avoid repeated calldata reads
for (uint256 i = 0; i < length;) {
IBatchExecution.Execution calldata execution = _executions[i];
uint256 ethAmount = execution.ethAmount;
address outputContract = execution.outputContract;
(bool success, bytes memory result) = ethAmount == 0
? outputContract.call(execution.arguments)
: outputContract.call{value: ethAmount}(execution.arguments);
results[i] = result;
if (!success) revert ExecutionFailed();
unchecked { ++i; }
}
return (true, results);
}
/**
* @dev Needed to allow the smart wallet to receive ETH and ERC1155/721 tokens
*/
receive() external payable {
// Allow receiving ETH
}
// ERC721 Receiver function
function onERC721Received(
address, /* operator */
address, /* from */
uint256, /* tokenId */
bytes calldata /* data */
) external pure override returns (bytes4) {
return 0x150b7a02;
}
// ERC1155 Receiver function
function onERC1155Received(
address, /* operator */
address, /* from */
uint256, /* id */
uint256, /* value */
bytes calldata /* data */
) external pure override returns (bytes4) {
return 0xf23a6e61;
}
// ERC1155 Batch Receiver function
function onERC1155BatchReceived(
address, /* operator */
address, /* from */
uint256[] calldata, /* ids */
uint256[] calldata, /* values */
bytes calldata /* data */
) external pure override returns (bytes4) {
return 0xbc197c81;
}
}
"
},
"lib/solady/src/utils/ECDSA.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Gas optimized ECDSA wrapper.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ECDSA.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ECDSA.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/ECDSA.sol)
///
/// @dev Note:
/// - The recovery functions use the ecrecover precompile (0x1).
/// - As of Solady version 0.0.68, the `recover` variants will revert upon recovery failure.
/// This is for more safety by default.
/// Use the `tryRecover` variants if you need to get the zero address back
/// upon recovery failure instead.
/// - As of Solady version 0.0.134, all `bytes signature` variants accept both
/// regular 65-byte `(r, s, v)` and EIP-2098 `(r, vs)` short form signatures.
/// See: https://eips.ethereum.org/EIPS/eip-2098
/// This is for calldata efficiency on smart accounts prevalent on L2s.
///
/// WARNING! Do NOT directly use signatures as unique identifiers:
/// - The recovery operations do NOT check if a signature is non-malleable.
/// - Use a nonce in the digest to prevent replay attacks on the same contract.
/// - Use EIP-712 for the digest to prevent replay attacks across different chains and contracts.
/// EIP-712 also enables readable signing of typed data for better user safety.
/// - If you need a unique hash from a signature, please use the `canonicalHash` functions.
library ECDSA {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The order of the secp256k1 elliptic curve.
uint256 internal constant N = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141;
/// @dev `N/2 + 1`. Used for checking the malleability of the signature.
uint256 private constant _HALF_N_PLUS_1 =
0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The signature is invalid.
error InvalidSignature();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* RECOVERY OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
function recover(bytes32 hash, bytes memory signature) internal view returns (address result) {
/// @solidity memory-safe-assembly
assembly {
for { let m := mload(0x40) } 1 {
mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
revert(0x1c, 0x04)
} {
switch mload(signature)
case 64 {
let vs := mload(add(signature, 0x40))
mstore(0x20, add(shr(255, vs), 27)) // `v`.
mstore(0x60, shr(1, shl(1, vs))) // `s`.
}
case 65 {
mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
mstore(0x60, mload(add(signature, 0x40))) // `s`.
}
default { continue }
mstore(0x00, hash)
mstore(0x40, mload(add(signature, 0x20))) // `r`.
result := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
mstore(0x60, 0) // Restore the zero slot.
mstore(0x40, m) // Restore the free memory pointer.
// `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
if returndatasize() { break }
}
}
}
/// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
function recoverCalldata(bytes32 hash, bytes calldata signature)
internal
view
returns (address result)
{
/// @solidity memory-safe-assembly
assembly {
for { let m := mload(0x40) } 1 {
mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
revert(0x1c, 0x04)
} {
switch signature.length
case 64 {
let vs := calldataload(add(signature.offset, 0x20))
mstore(0x20, add(shr(255, vs), 27)) // `v`.
mstore(0x40, calldataload(signature.offset)) // `r`.
mstore(0x60, shr(1, shl(1, vs))) // `s`.
}
case 65 {
mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`.
}
default { continue }
mstore(0x00, hash)
result := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
mstore(0x60, 0) // Restore the zero slot.
mstore(0x40, m) // Restore the free memory pointer.
// `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
if returndatasize() { break }
}
}
}
/// @dev Recovers the signer's address from a message digest `hash`,
/// and the EIP-2098 short form signature defined by `r` and `vs`.
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal view returns (address result) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x00, hash)
mstore(0x20, add(shr(255, vs), 27)) // `v`.
mstore(0x40, r)
mstore(0x60, shr(1, shl(1, vs))) // `s`.
result := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
// `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
if iszero(returndatasize()) {
mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
revert(0x1c, 0x04)
}
mstore(0x60, 0) // Restore the zero slot.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Recovers the signer's address from a message digest `hash`,
/// and the signature defined by `v`, `r`, `s`.
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s)
internal
view
returns (address result)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x00, hash)
mstore(0x20, and(v, 0xff))
mstore(0x40, r)
mstore(0x60, s)
result := mload(staticcall(gas(), 1, 0x00, 0x80, 0x01, 0x20))
// `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
if iszero(returndatasize()) {
mstore(0x00, 0x8baa579f) // `InvalidSignature()`.
revert(0x1c, 0x04)
}
mstore(0x60, 0) // Restore the zero slot.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* TRY-RECOVER OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// WARNING!
// These functions will NOT revert upon recovery failure.
// Instead, they will return the zero address upon recovery failure.
// It is critical that the returned address is NEVER compared against
// a zero address (e.g. an uninitialized address variable).
/// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
function tryRecover(bytes32 hash, bytes memory signature)
internal
view
returns (address result)
{
/// @solidity memory-safe-assembly
assembly {
for { let m := mload(0x40) } 1 {} {
switch mload(signature)
case 64 {
let vs := mload(add(signature, 0x40))
mstore(0x20, add(shr(255, vs), 27)) // `v`.
mstore(0x60, shr(1, shl(1, vs))) // `s`.
}
case 65 {
mstore(0x20, byte(0, mload(add(signature, 0x60)))) // `v`.
mstore(0x60, mload(add(signature, 0x40))) // `s`.
}
default { break }
mstore(0x00, hash)
mstore(0x40, mload(add(signature, 0x20))) // `r`.
pop(staticcall(gas(), 1, 0x00, 0x80, 0x40, 0x20))
mstore(0x60, 0) // Restore the zero slot.
// `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
result := mload(xor(0x60, returndatasize()))
mstore(0x40, m) // Restore the free memory pointer.
break
}
}
}
/// @dev Recovers the signer's address from a message digest `hash`, and the `signature`.
function tryRecoverCalldata(bytes32 hash, bytes calldata signature)
internal
view
returns (address result)
{
/// @solidity memory-safe-assembly
assembly {
for { let m := mload(0x40) } 1 {} {
switch signature.length
case 64 {
let vs := calldataload(add(signature.offset, 0x20))
mstore(0x20, add(shr(255, vs), 27)) // `v`.
mstore(0x40, calldataload(signature.offset)) // `r`.
mstore(0x60, shr(1, shl(1, vs))) // `s`.
}
case 65 {
mstore(0x20, byte(0, calldataload(add(signature.offset, 0x40)))) // `v`.
calldatacopy(0x40, signature.offset, 0x40) // Copy `r` and `s`.
}
default { break }
mstore(0x00, hash)
pop(staticcall(gas(), 1, 0x00, 0x80, 0x40, 0x20))
mstore(0x60, 0) // Restore the zero slot.
// `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
result := mload(xor(0x60, returndatasize()))
mstore(0x40, m) // Restore the free memory pointer.
break
}
}
}
/// @dev Recovers the signer's address from a message digest `hash`,
/// and the EIP-2098 short form signature defined by `r` and `vs`.
function tryRecover(bytes32 hash, bytes32 r, bytes32 vs)
internal
view
returns (address result)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x00, hash)
mstore(0x20, add(shr(255, vs), 27)) // `v`.
mstore(0x40, r)
mstore(0x60, shr(1, shl(1, vs))) // `s`.
pop(staticcall(gas(), 1, 0x00, 0x80, 0x40, 0x20))
mstore(0x60, 0) // Restore the zero slot.
// `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
result := mload(xor(0x60, returndatasize()))
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Recovers the signer's address from a message digest `hash`,
/// and the signature defined by `v`, `r`, `s`.
function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s)
internal
view
returns (address result)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x00, hash)
mstore(0x20, and(v, 0xff))
mstore(0x40, r)
mstore(0x60, s)
pop(staticcall(gas(), 1, 0x00, 0x80, 0x40, 0x20))
mstore(0x60, 0) // Restore the zero slot.
// `returndatasize()` will be `0x20` upon success, and `0x00` otherwise.
result := mload(xor(0x60, returndatasize()))
mstore(0x40, m) // Restore the free memory pointer.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* HASHING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns an Ethereum Signed Message, created from a `hash`.
/// This produces a hash corresponding to the one signed with the
/// [`eth_sign`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign)
/// JSON-RPC method as part of EIP-191.
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x20, hash) // Store into scratch space for keccak256.
mstore(0x00, "\x00\x00\x00\x00\x19Ethereum Signed Message:\
32") // 28 bytes.
result := keccak256(0x04, 0x3c) // `32 * 2 - (32 - 28) = 60 = 0x3c`.
}
}
/// @dev Returns an Ethereum Signed Message, created from `s`.
/// This produces a hash corresponding to the one signed with the
/// [`eth_sign`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign)
/// JSON-RPC method as part of EIP-191.
/// Note: Supports lengths of `s` up to 999999 bytes.
function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let sLength := mload(s)
let o := 0x20
mstore(o, "\x19Ethereum Signed Message:\
") // 26 bytes, zero-right-padded.
mstore(0x00, 0x00)
// Convert the `s.length` to ASCII decimal representation: `base10(s.length)`.
for { let temp := sLength } 1 {} {
o := sub(o, 1)
mstore8(o, add(48, mod(temp, 10)))
temp := div(temp, 10)
if iszero(temp) { break }
}
let n := sub(0x3a, o) // Header length: `26 + 32 - o`.
// Throw an out-of-offset error (consumes all gas) if the header exceeds 32 bytes.
returndatacopy(returndatasize(), returndatasize(), gt(n, 0x20))
mstore(s, or(mload(0x00), mload(n))) // Temporarily store the header.
result := keccak256(add(s, sub(0x20, n)), add(n, sLength))
mstore(s, sLength) // Restore the length.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CANONICAL HASH FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// The following functions return the hash of the signature in its canonicalized format,
// which is the 65-byte `abi.encodePacked(r, s, uint8(v))`, where `v` is either 27 or 28.
// If `s` is greater than `N / 2` then it will be converted to `N - s`
// and the `v` value will be flipped.
// If the signature has an invalid length, or if `v` is invalid,
// a uniquely corrupt hash will be returned.
// These functions are useful for "poor-mans-VRF".
/// @dev Returns the canonical hash of `signature`.
function canonicalHash(bytes memory signature) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
let l := mload(signature)
for {} 1 {} {
mstore(0x00, mload(add(signature, 0x20))) // `r`.
let s := mload(add(signature, 0x40))
let v := mload(add(signature, 0x41))
if eq(l, 64) {
v := add(shr(255, s), 27)
s := shr(1, shl(1, s))
}
if iszero(lt(s, _HALF_N_PLUS_1)) {
v := xor(v, 7)
s := sub(N, s)
}
mstore(0x21, v)
mstore(0x20, s)
result := keccak256(0x00, 0x41)
mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
break
}
// If the length is neither 64 nor 65, return a uniquely corrupted hash.
if iszero(lt(sub(l, 64), 2)) {
// `bytes4(keccak256("InvalidSignatureLength"))`.
result := xor(keccak256(add(signature, 0x20), l), 0xd62f1ab2)
}
}
}
/// @dev Returns the canonical hash of `signature`.
function canonicalHashCalldata(bytes calldata signature)
internal
pure
returns (bytes32 result)
{
/// @solidity memory-safe-assembly
assembly {
for {} 1 {} {
mstore(0x00, calldataload(signature.offset)) // `r`.
let s := calldataload(add(signature.offset, 0x20))
let v := calldataload(add(signature.offset, 0x21))
if eq(signature.length, 64) {
v := add(shr(255, s), 27)
s := shr(1, shl(1, s))
}
if iszero(lt(s, _HALF_N_PLUS_1)) {
v := xor(v, 7)
s := sub(N, s)
}
mstore(0x21, v)
mstore(0x20, s)
result := keccak256(0x00, 0x41)
mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
break
}
// If the length is neither 64 nor 65, return a uniquely corrupted hash.
if iszero(lt(sub(signature.length, 64), 2)) {
calldatacopy(mload(0x40), signature.offset, signature.length)
// `bytes4(keccak256("InvalidSignatureLength"))`.
result := xor(keccak256(mload(0x40), signature.length), 0xd62f1ab2)
}
}
}
/// @dev Returns the canonical hash of `signature`.
function canonicalHash(bytes32 r, bytes32 vs) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, r) // `r`.
let v := add(shr(255, vs), 27)
let s := shr(1, shl(1, vs))
mstore(0x21, v)
mstore(0x20, s)
result := keccak256(0x00, 0x41)
mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
}
}
/// @dev Returns the canonical hash of `signature`.
function canonicalHash(uint8 v, bytes32 r, bytes32 s) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, r) // `r`.
if iszero(lt(s, _HALF_N_PLUS_1)) {
v := xor(v, 7)
s := sub(N, s)
}
mstore(0x21, v)
mstore(0x20, s)
result := keccak256(0x00, 0x41)
mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EMPTY CALLDATA HELPERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns an empty calldata bytes.
function emptySignature() internal pure returns (bytes calldata signature) {
/// @solidity memory-safe-assembly
assembly {
signature.length := 0
}
}
}
"
},
"lib/solady/src/utils/EIP712.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Contract for EIP-712 typed structured data hashing and signing.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/EIP712.sol)
/// @author Modified from Solbase (https://github.com/Sol-DAO/solbase/blob/main/src/utils/EIP712.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/EIP712.sol)
///
/// @dev Note, this implementation:
/// - Uses `address(this)` for the `verifyingContract` field.
/// - Does NOT use the optional EIP-712 salt.
/// - Does NOT use any EIP-712 extensions.
/// This is for simplicity and to save gas.
/// If you need to customize, please fork / modify accordingly.
abstract contract EIP712 {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS AND IMMUTABLES */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`.
bytes32 internal constant _DOMAIN_TYPEHASH =
0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;
/// @dev `keccak256("EIP712Domain(string name,string version,address verifyingContract)")`.
/// This is only used in `_hashTypedDataSansChainId`.
bytes32 internal constant _DOMAIN_TYPEHASH_SANS_CHAIN_ID =
0x91ab3d17e3a50a9d89e63fd30b92be7f5336b03b287bb946787a83a9d62a2766;
/// @dev `keccak256("EIP712Domain(string name,string version)")`.
/// This is only used in `_hashTypedDataSansChainIdAndVerifyingContract`.
bytes32 internal constant _DOMAIN_TYPEHASH_SANS_CHAIN_ID_AND_VERIFYING_CONTRACT =
0xb03948446334eb9b2196d5eb166f69b9d49403eb4a12f36de8d3f9f3cb8e15c3;
/// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId)")`.
/// This is only used in `_hashTypedDataSansVerifyingContract`.
bytes32 internal constant _DOMAIN_TYPEHASH_SANS_VERIFYING_CONTRACT =
0xc2f8787176b8ac6bf7215b4adcc1e069bf4ab82d9ab1df05a57a91d425935b6e;
uint256 private immutable _cachedThis;
uint256 private immutable _cachedChainId;
bytes32 private immutable _cachedNameHash;
bytes32 private immutable _cachedVersionHash;
bytes32 private immutable _cachedDomainSeparator;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTRUCTOR */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Cache the hashes for cheaper runtime gas costs.
/// In the case of upgradeable contracts (i.e. proxies),
/// or if the chain id changes due to a hard fork,
/// the domain separator will be seamlessly calculated on-the-fly.
constructor() {
_cachedThis = uint256(uint160(address(this)));
_cachedChainId = block.chainid;
string memory name;
string memory version;
if (!_domainNameAndVersionMayChange()) (name, version) = _domainNameAndVersion();
bytes32 nameHash = _domainNameAndVersionMayChange() ? bytes32(0) : keccak256(bytes(name));
bytes32 versionHash =
_domainNameAndVersionMayChange() ? bytes32(0) : keccak256(bytes(version));
_cachedNameHash = nameHash;
_cachedVersionHash = versionHash;
bytes32 separator;
if (!_domainNameAndVersionMayChange()) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Load the free memory pointer.
mstore(m, _DOMAIN_TYPEHASH)
mstore(add(m, 0x20), nameHash)
mstore(add(m, 0x40), versionHash)
mstore(add(m, 0x60), chainid())
mstore(add(m, 0x80), address())
separator := keccak256(m, 0xa0)
}
}
_cachedDomainSeparator = separator;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* FUNCTIONS TO OVERRIDE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Please override this function to return the domain name and version.
/// ```
/// function _domainNameAndVersion()
/// internal
/// pure
/// virtual
/// returns (string memory name, string memory version)
/// {
/// name = "Solady";
/// version = "1";
/// }
/// ```
///
/// Note: If the returned result may change after the contract has been deployed,
/// you must override `_domainNameAndVersionMayChange()` to return true.
function _domainNameAndVersion()
internal
view
virtual
returns (string memory name, string memory version);
/// @dev Returns if `_domainNameAndVersion()` may change
/// after the contract has been deployed (i.e. after the constructor).
/// Default: false.
function _domainNameAndVersionMayChange() internal pure virtual returns (bool result) {}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* HASHING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the EIP-712 domain separator.
function _domainSeparator() internal view virtual returns (bytes32 separator) {
if (_domainNameAndVersionMayChange()) {
separator = _buildDomainSeparator();
} else {
separator = _cachedDomainSeparator;
if (_cachedDomainSeparatorInvalidated()) separator = _buildDomainSeparator();
}
}
/// @dev Returns the hash of the fully encoded EIP-712 message for this domain,
/// given `structHash`, as defined in
/// https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct.
///
/// The hash can be used together with {ECDSA-recover} to obtain the signer of a message:
/// ```
/// bytes32 digest = _hashTypedData(keccak256(abi.encode(
/// keccak256("Mail(address to,string contents)"),
/// mailTo,
/// keccak256(bytes(mailContents))
/// )));
/// address signer = ECDSA.recover(digest, signature);
/// ```
function _hashTypedData(bytes32 structHash) internal view virtual returns (bytes32 digest) {
// We will use `digest` to store the domain separator to save a bit of gas.
if (_domainNameAndVersionMayChange()) {
digest = _buildDomainSeparator();
} else {
digest = _cachedDomainSeparator;
if (_cachedDomainSeparatorInvalidated()) digest = _buildDomainSeparator();
}
/// @solidity memory-safe-assembly
assembly {
// Compute the digest.
mstore(0x00, 0x1901000000000000) // Store "\x19\x01".
mstore(0x1a, digest) // Store the domain separator.
mstore(0x3a, structHash) // Store the struct hash.
digest := keccak256(0x18, 0x42)
// Restore the part of the free memory slot that was overwritten.
mstore(0x3a, 0)
}
}
/// @dev Variant of `_hashTypedData` that excludes the chain ID.
/// Included for the niche use case of cross-chain workflows.
function _hashTypedDataSansChainId(bytes32 structHash)
internal
view
virtual
returns (bytes32 digest)
{
(string memory name, string memory version) = _domainNameAndVersion();
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Load the free memory pointer.
mstore(0x00, _DOMAIN_TYPEHASH_SANS_CHAIN_ID)
mstore(0x20, keccak256(add(name, 0x20), mload(name)))
mstore(0x40, keccak256(add(version, 0x20), mload(version)))
mstore(0x60, address())
// Compute the digest.
mstore(0x20, keccak256(0x00, 0x80)) // Store the domain separator.
mstore(0x00, 0x1901) // Store "\x19\x01".
mstore(0x40, structHash) // Store the struct hash.
digest := keccak256(0x1e, 0x42)
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero pointer.
}
}
/// @dev Variant of `_hashTypedData` that excludes the chain ID and verifying contract.
/// Included for the niche use case of cross-chain and multi-verifier workflows.
function _hashTypedDataSansChainIdAndVerifyingContract(bytes32 structHash)
internal
view
virtual
returns (bytes32 digest)
{
(string memory name, string memory version) = _domainNameAndVersion();
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Load the free memory pointer.
mstore(0x00, _DOMAIN_TYPEHASH_SANS_CHAIN_ID_AND_VERIFYING_CONTRACT)
mstore(0x20, keccak256(add(name, 0x20), mload(name)))
mstore(0x40, keccak256(add(version, 0x20), mload(version)))
// Compute the digest.
mstore(0x20, keccak256(0x00, 0x60)) // Store the domain separator.
mstore(0x00, 0x1901) // Store "\x19\x01".
mstore(0x40, structHash) // Store the struct hash.
digest := keccak256(0x1e, 0x42)
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero pointer.
}
}
/// @dev Variant of `_hashTypedData` that excludes the chain ID and verifying contract.
/// Included for the niche use case of multi-verifier workflows.
function _hashTypedDataSansVerifyingContract(bytes32 structHash)
internal
view
virtual
returns (bytes32 digest)
{
(string memory name, string memory version) = _domainNameAndVersion();
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Load the free memory pointer.
mstore(0x00, _DOMAIN_TYPEHASH_SANS_VERIFYING_CONTRACT)
mstore(0x20, keccak256(add(name, 0x20), mload(name)))
mstore(0x40, keccak256(add(version, 0x20), mload(version)))
mstore(0x60, chainid())
// Compute the digest.
mstore(0x20, keccak256(0x00, 0x80)) // Store the domain separator.
mstore(0x00, 0x1901) // Store "\x19\x01".
mstore(0x40, structHash) // Store the struct hash.
digest := keccak256(0x1e, 0x42)
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero pointer.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EIP-5267 OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev See: https://eips.ethereum.org/EIPS/eip-5267
function eip712Domain()
public
view
virtual
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
)
{
fields = hex"0f"; // `0b01111`.
(name, version) = _domainNameAndVersion();
chainId = block.chainid;
verifyingContract = address(this);
salt = salt; // `bytes32(0)`.
extensions = extensions; // `new uint256[](0)`.
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PRIVATE HELPERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the EIP-712 domain separator.
function _buildDomainSeparator() private view returns (bytes32 separator) {
// We will use `separator` to store the name hash to save a bit of gas.
bytes32 versionHash;
if (_domainNameAndVersionMayChange()) {
(string memory name, string memory version) = _domainNameAndVersion();
separator = keccak256(bytes(name));
versionHash = keccak256(bytes(version));
} else {
separator = _cachedNameHash;
versionHash = _cachedVersionHash;
}
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Load the free memory pointer.
mstore(m, _DOMAIN_TYPEHASH)
mstore(add(m, 0x20), separator) // Name hash.
mstore(add(m, 0x40), versionHash)
mstore(add(m, 0x60), chainid())
mstore(add(m, 0x80), address())
separator := keccak256(m, 0xa0)
}
}
/// @dev Returns if the cached domain separator has been invalidated.
function _cachedDomainSeparatorInvalidated() private view returns (bool result) {
uint256 cachedChainId = _cachedChainId;
uint256 cachedThis = _cachedThis;
/// @solidity memory-safe-assembly
assembly {
result := iszero(and(eq(chainid(), cachedChainId), eq(address(), cachedThis)))
}
}
}
"
},
"src/TKGasStation/IBatchExecution.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
// Shared interface for batch execution structures
interface IBatchExecution {
struct Execution {
address outputContract;
uint256 ethAmount;
bytes arguments;
}
}
"
}
},
"settings": {
"remappings": [
"solady/=lib/solady/src/",
"@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"account-abstraction/=lib/account-abstraction/contracts/",
"erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
"forge-std/=lib/forge-std/src/",
"halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/",
"openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/"
],
"optimizer": {
"enabled": false,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "prague",
"viaIR": true
}
}}
Submitted on: 2025-09-25 09:55:10
Comments
Log in to comment.
No comments yet.