QuiverV4Gate

0x220a377e0ea1085bb5d2723b724269b8a6240db1

Verification
Verified
v0.8.26+commit.8a97fa7a
Type
Contract
12,916 bytes
ABI entries
65
19 read · 10 write
License
none

Contract information

Address
0x220a377e0ea1085bb5d2723b724269b8a6240db1
Chain
Robinhood Chain (4663)
Compiler
v0.8.26+commit.8a97fa7a
Optimization
Enabled
Creation tx
0x57f0b1961c…23a373d21a

Token

Not a token

This contract does not expose ERC-20 metadata.

Read contract (19)

BUY_TYPEHASH()bytes32
MAX_VIEW_PAGE()uint256
creatorOfPool(bytes32)address
customSignerOfPool(bytes32)address
effectiveSigner(bytes32)address
eip712Domain()bytes1, string, string, uint256, address, bytes32, uint256[]
hashVoucher(tuple)bytes32
hook()address
isAuthorized(address, bytes32, uint256)bool
launcher()address
owner()address
pendingOwner()address
poolSignerStatesBatch(bytes32[])tuple[]
signer()address
signerRotationLocked(bytes32)bool
tokenOfPool(bytes32)address
universalRouter()address
usedNonces(address, uint256)bool
weth()address

Events (10)

BuyExecutedEIP712DomainChangedHookSetLauncherSetOwnershipTransferStartedOwnershipTransferredPoolRegisteredPoolSignerChangedPoolSignerLockedSignerChanged

ABI

[
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "universalRouter_",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "weth_",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "signer_",
        "type": "address"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "target",
        "type": "address"
      }
    ],
    "name": "AddressEmptyCode",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "account",
        "type": "address"
      }
    ],
    "name": "AddressInsufficientBalance",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "ECDSAInvalidSignature",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "length",
        "type": "uint256"
      }
    ],
    "name": "ECDSAInvalidSignatureLength",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "s",
        "type": "bytes32"
      }
    ],
    "name": "ECDSAInvalidSignatureS",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "FailedInnerCall",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "HookAlreadySet",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidPool",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidShortString",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidVoucher",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "LauncherAlreadySet",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "OnlyCreator",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "OnlyHook",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "OnlyLauncher",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "owner",
        "type": "address"
      }
    ],
    "name": "OwnableInvalidOwner",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "account",
        "type": "address"
      }
    ],
    "name": "OwnableUnauthorizedAccount",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "PageTooLarge",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "Reentrancy",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      }
    ],
    "name": "SafeERC20FailedOperation",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "SignerRotationLocked",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "string",
        "name": "str",
        "type": "string"
      }
    ],
    "name": "StringTooLong",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "TransferFailed",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "VoucherExpired",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "VoucherUsed",
    "type": "error"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "buyer",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "PoolId",
        "name": "poolId",
        "type": "bytes32"
      },
      {
        "indexed": true,
        "internalType": "uint256",
        "name": "nonce",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "wethSpent",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "tokenOut",
        "type": "uint256"
      }
    ],
    "name": "BuyExecuted",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [],
    "name": "EIP712DomainChanged",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "hook",
        "type": "address"
      }
    ],
    "name": "HookSet",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "launcher",
        "type": "address"
      }
    ],
    "name": "LauncherSet",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "previousOwner",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "newOwner",
        "type": "address"
      }
    ],
    "name": "OwnershipTransferStarted",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "previousOwner",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "newOwner",
        "type": "address"
      }
    ],
    "name": "OwnershipTransferred",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "PoolId",
        "name": "poolId",
        "type": "bytes32"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "creator",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "address",
        "name": "customSigner",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "bool",
        "name": "rotationLocked",
        "type": "bool"
      }
    ],
    "name": "PoolRegistered",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "PoolId",
        "name": "poolId",
        "type": "bytes32"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "previousSigner",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "newSigner",
        "type": "address"
      }
    ],
    "name": "PoolSignerChanged",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "PoolId",
        "name": "poolId",
        "type": "bytes32"
      }
    ],
    "name": "PoolSignerLocked",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "previousSigner",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "newSigner",
        "type": "address"
      }
    ],
    "name": "SignerChanged",
    "type": "event"
  },
  {
    "inputs": [],
    "name": "BUY_TYPEHASH",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "MAX_VIEW_PAGE",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "acceptOwnership",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "components": [
          {
            "internalType": "Currency",
            "name": "currency0",
            "type": "address"
          },
          {
            "internalType": "Currency",
            "name": "currency1",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          },
          {
            "internalType": "contract IHooks",
            "name": "hooks",
            "type": "address"
          }
        ],
        "internalType": "struct PoolKey",
        "name": "key",
        "type": "tuple"
      },
      {
        "components": [
          {
            "internalType": "address",
            "name": "buyer",
            "type": "address"
          },
          {
            "internalType": "PoolId",
            "name": "poolId",
            "type": "bytes32"
          },
          {
            "internalType": "address",
            "name": "token",
            "type": "address"
          },
          {
            "internalType": "uint128",
            "name": "tokenOut",
            "type": "uint128"
          },
          {
            "internalType": "uint128",
            "name": "maxWethIn",
            "type": "uint128"
          },
          {
            "internalType": "uint256",
            "name": "nonce",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "deadline",
            "type": "uint256"
          }
        ],
        "internalType": "struct QuiverV4Gate.BuyVoucher",
        "name": "voucher",
        "type": "tuple"
      },
      {
        "internalType": "bytes",
        "name": "signature",
        "type": "bytes"
      }
    ],
    "name": "buy",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "tokenOut",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "wethSpent",
        "type": "uint256"
      }
    ],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "PoolId",
        "name": "",
        "type": "bytes32"
      }
    ],
    "name": "creatorOfPool",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "PoolId",
        "name": "",
        "type": "bytes32"
      }
    ],
    "name": "customSignerOfPool",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "PoolId",
        "name": "poolId",
        "type": "bytes32"
      }
    ],
    "name": "effectiveSigner",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "eip712Domain",
    "outputs": [
      {
        "internalType": "bytes1",
        "name": "fields",
        "type": "bytes1"
      },
      {
        "internalType": "string",
        "name": "name",
        "type": "string"
      },
      {
        "internalType": "string",
        "name": "version",
        "type": "string"
      },
      {
        "internalType": "uint256",
        "name": "chainId",
        "type": "uint256"
      },
      {
        "internalType": "address",
        "name": "verifyingContract",
        "type": "address"
      },
      {
        "internalType": "bytes32",
        "name": "salt",
        "type": "bytes32"
      },
      {
        "internalType": "uint256[]",
        "name": "extensions",
        "type": "uint256[]"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "components": [
          {
            "internalType": "address",
            "name": "buyer",
            "type": "address"
          },
          {
            "internalType": "PoolId",
            "name": "poolId",
            "type": "bytes32"
          },
          {
            "internalType": "address",
            "name": "token",
            "type": "address"
          },
          {
            "internalType": "uint128",
            "name": "tokenOut",
            "type": "uint128"
          },
          {
            "internalType": "uint128",
            "name": "maxWethIn",
            "type": "uint128"
          },
          {
            "internalType": "uint256",
            "name": "nonce",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "deadline",
            "type": "uint256"
          }
        ],
        "internalType": "struct QuiverV4Gate.BuyVoucher",
        "name": "voucher",
        "type": "tuple"
      }
    ],
    "name": "hashVoucher",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "hook",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "buyer",
        "type": "address"
      },
      {
        "internalType": "PoolId",
        "name": "poolId",
        "type": "bytes32"
      },
      {
        "internalType": "uint256",
        "name": "nonce",
        "type": "uint256"
      }
    ],
    "name": "isAuthorized",
    "outputs": [
      {
        "internalType": "bool",
        "name": "authorized",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "launcher",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "PoolId",
        "name": "poolId",
        "type": "bytes32"
      }
    ],
    "name": "lockPoolSigner",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "owner",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "pendingOwner",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "PoolId[]",
        "name": "poolIds",
        "type": "bytes32[]"
      }
    ],
    "name": "poolSignerStatesBatch",
    "outputs": [
      {
        "components": [
          {
            "internalType": "address",
            "name": "creator",
            "type": "address"
          },
          {
            "internalType": "address",
            "name": "customSigner",
            "type": "address"
          },
          {
            "internalType": "address",
            "name": "effectiveSigner",
            "type": "address"
          },
          {
            "internalType": "bool",
            "name": "rotationLocked",
            "type": "bool"
          }
        ],
        "internalType": "struct QuiverV4Gate.PoolSignerState[]",
        "name": "states",
        "type": "tuple[]"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "components": [
          {
            "internalType": "Currency",
            "name": "currency0",
            "type": "address"
          },
          {
            "internalType": "Currency",
            "name": "currency1",
            "type": "address"
          },
          {
            "internalType": "uint24",
            "name": "fee",
            "type": "uint24"
          },
          {
            "internalType": "int24",
            "name": "tickSpacing",
            "type": "int24"
          },
          {
            "internalType": "contract IHooks",
            "name": "hooks",
            "type": "address"
          }
        ],
        "internalType": "struct PoolKey",
        "name": "key",
        "type": "tuple"
      },
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "creator",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "gateSigner",
        "type": "address"
      },
      {
        "internalType": "bool",
        "name": "gated",
        "type": "bool"
      }
    ],
    "name": "registerPool",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "renounceOwnership",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "hook_",
        "type": "address"
      }
    ],
    "name": "setHook",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "launcher_",
        "type": "address"
      }
    ],
    "name": "setLauncher",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "PoolId",
        "name": "poolId",
        "type": "bytes32"
      },
      {
        "internalType": "address",
        "name": "newSigner",
        "type": "address"
      }
    ],
    "name": "setPoolSigner",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "signer_",
        "type": "address"
      }
    ],
    "name": "setSigner",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "signer",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "PoolId",
        "name": "",
        "type": "bytes32"
      }
    ],
    "name": "signerRotationLocked",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "PoolId",
        "name": "",
        "type": "bytes32"
      }
    ],
    "name": "tokenOfPool",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "newOwner",
        "type": "address"
      }
    ],
    "name": "transferOwnership",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "universalRouter",
    "outputs": [
      {
        "internalType": "contract IUniversalRouterV211",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "name": "usedNonces",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "weth",
    "outputs": [
      {
        "internalType": "contract IWETH9",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "stateMutability": "payable",
    "type": "receive"
  }
]

Source code

// SPDX-License-Identifier: BUSL-1.1
// Copyright (c) 2026 Quiver (quiver-app.io)
pragma solidity 0.8.26;

import { Ownable2Step } from "openzeppelin-contracts/contracts/access/Ownable2Step.sol";
import { Ownable } from "openzeppelin-contracts/contracts/access/Ownable.sol";
import { EIP712 } from "openzeppelin-contracts/contracts/utils/cryptography/EIP712.sol";
import { ECDSA } from "openzeppelin-contracts/contracts/utils/cryptography/ECDSA.sol";
import { IERC20 } from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
import { PoolKey } from "v4-core/src/types/PoolKey.sol";
import { PoolId, PoolIdLibrary } from "v4-core/src/types/PoolId.sol";
import { Currency } from "v4-core/src/types/Currency.sol";
import { UniversalRouterV211Encoder } from "./libraries/UniversalRouterV211Encoder.sol";

interface IUniversalRouterV211 {
    function execute(bytes calldata commands, bytes[] calldata inputs, uint256 deadline) external payable;
}

interface IWETH9 is IERC20 {
    function withdraw(uint256 amount) external;
}

/// @notice EIP-712 voucher gateway for gated Quiver v4 buys.
contract QuiverV4Gate is EIP712, Ownable2Step {
    using PoolIdLibrary for PoolKey;
    using SafeERC20 for IERC20;

    uint256 public constant MAX_VIEW_PAGE = 500;

    bytes32 public constant BUY_TYPEHASH = keccak256(
        "Buy(address buyer,bytes32 poolId,address token,uint128 tokenOut,uint128 maxWethIn,uint256 nonce,uint256 deadline)"
    );

    IUniversalRouterV211 public immutable universalRouter;
    IWETH9 public immutable weth;
    address public signer;
    address public launcher;
    address public hook;

    mapping(PoolId => address) public tokenOfPool;
    mapping(PoolId => address) public creatorOfPool;
    mapping(PoolId => address) public customSignerOfPool;
    mapping(PoolId => bool) public signerRotationLocked;
    mapping(address => mapping(uint256 => bool)) public usedNonces;

    struct BuyVoucher {
        address buyer;
        PoolId poolId;
        address token;
        uint128 tokenOut;
        uint128 maxWethIn;
        uint256 nonce;
        uint256 deadline;
    }

    struct PoolSignerState {
        address creator;
        address customSigner;
        address effectiveSigner;
        bool rotationLocked;
    }

    event SignerChanged(address indexed previousSigner, address indexed newSigner);
    event LauncherSet(address indexed launcher);
    event HookSet(address indexed hook);
    event PoolRegistered(
        PoolId indexed poolId, address indexed token, address indexed creator, address customSigner, bool rotationLocked
    );
    event PoolSignerChanged(PoolId indexed poolId, address indexed previousSigner, address indexed newSigner);
    event PoolSignerLocked(PoolId indexed poolId);
    event BuyExecuted(
        address indexed buyer, PoolId indexed poolId, uint256 indexed nonce, uint256 wethSpent, uint256 tokenOut
    );

    error OnlyLauncher();
    error OnlyHook();
    error OnlyCreator();
    error LauncherAlreadySet();
    error HookAlreadySet();
    error SignerRotationLocked();
    error InvalidVoucher();
    error VoucherExpired();
    error VoucherUsed();
    error InvalidPool();
    error TransferFailed();
    error Reentrancy();
    error PageTooLarge();

    uint256 private _entered = 1;

    constructor(address universalRouter_, address weth_, address signer_)
        EIP712("QuiverV4Gate", "1")
        Ownable(msg.sender)
    {
        if (universalRouter_ == address(0) || weth_ == address(0) || signer_ == address(0)) {
            revert InvalidVoucher();
        }
        universalRouter = IUniversalRouterV211(universalRouter_);
        weth = IWETH9(weth_);
        signer = signer_;
    }

    receive() external payable {
        if (msg.sender != address(weth)) revert TransferFailed();
    }

    modifier nonReentrant() {
        if (_entered != 1) revert Reentrancy();
        _entered = 2;
        _;
        _entered = 1;
    }

    function setLauncher(address launcher_) external onlyOwner {
        if (launcher != address(0)) revert LauncherAlreadySet();
        if (launcher_ == address(0)) revert InvalidVoucher();
        launcher = launcher_;
        emit LauncherSet(launcher_);
    }

    function setHook(address hook_) external onlyOwner {
        if (hook != address(0)) revert HookAlreadySet();
        if (hook_ == address(0)) revert InvalidVoucher();
        hook = hook_;
        emit HookSet(hook_);
    }

    function setSigner(address signer_) external onlyOwner {
        if (signer_ == address(0)) revert InvalidVoucher();
        emit SignerChanged(signer, signer_);
        signer = signer_;
    }

    function registerPool(PoolKey calldata key, address token, address creator, address gateSigner, bool gated)
        external
    {
        if (msg.sender != launcher) revert OnlyLauncher();
        PoolId poolId = key.toId();
        if (token == address(0) || creator == address(0) || tokenOfPool[poolId] != address(0)) revert InvalidPool();
        tokenOfPool[poolId] = token;
        creatorOfPool[poolId] = creator;
        customSignerOfPool[poolId] = gateSigner;
        signerRotationLocked[poolId] = !gated;
        emit PoolRegistered(poolId, token, creator, gateSigner, !gated);
    }

    function setPoolSigner(PoolId poolId, address newSigner) external {
        if (msg.sender != creatorOfPool[poolId]) revert OnlyCreator();
        if (signerRotationLocked[poolId]) revert SignerRotationLocked();
        address previousSigner = customSignerOfPool[poolId];
        customSignerOfPool[poolId] = newSigner;
        emit PoolSignerChanged(poolId, previousSigner, newSigner);
    }

    function lockPoolSigner(PoolId poolId) external {
        if (msg.sender != hook) revert OnlyHook();
        if (tokenOfPool[poolId] == address(0)) revert InvalidPool();
        if (!signerRotationLocked[poolId]) {
            signerRotationLocked[poolId] = true;
            emit PoolSignerLocked(poolId);
        }
    }

    function effectiveSigner(PoolId poolId) public view returns (address) {
        if (tokenOfPool[poolId] == address(0)) revert InvalidPool();
        address customSigner = customSignerOfPool[poolId];
        return customSigner == address(0) ? signer : customSigner;
    }

    function poolSignerStatesBatch(PoolId[] calldata poolIds) external view returns (PoolSignerState[] memory states) {
        if (poolIds.length > MAX_VIEW_PAGE) revert PageTooLarge();
        states = new PoolSignerState[](poolIds.length);
        for (uint256 index; index < poolIds.length; ++index) {
            PoolId poolId = poolIds[index];
            address customSigner = customSignerOfPool[poolId];
            states[index] = PoolSignerState({
                creator: creatorOfPool[poolId],
                customSigner: customSigner,
                effectiveSigner: customSigner == address(0) ? signer : customSigner,
                rotationLocked: signerRotationLocked[poolId]
            });
        }
    }

    /// @notice Executes an exact-output buy through the canonical Universal Router.
    /// Any unspent WETH is unwrapped and refunded to the buyer.
    function buy(PoolKey calldata key, BuyVoucher calldata voucher, bytes calldata signature)
        external
        payable
        nonReentrant
        returns (uint256 tokenOut, uint256 wethSpent)
    {
        if (msg.sender != voucher.buyer || block.timestamp > voucher.deadline) revert VoucherExpired();
        if (msg.value != voucher.maxWethIn || voucher.tokenOut == 0 || voucher.maxWethIn == 0) revert InvalidVoucher();

        PoolId poolId = key.toId();
        if (
            PoolId.unwrap(poolId) != PoolId.unwrap(voucher.poolId) || tokenOfPool[poolId] != voucher.token
                || usedNonces[voucher.buyer][voucher.nonce]
        ) {
            if (usedNonces[voucher.buyer][voucher.nonce]) revert VoucherUsed();
            revert InvalidPool();
        }
        bytes32 digest = _hashTypedDataV4(
            keccak256(
                abi.encode(
                    BUY_TYPEHASH,
                    voucher.buyer,
                    PoolId.unwrap(voucher.poolId),
                    voucher.token,
                    voucher.tokenOut,
                    voucher.maxWethIn,
                    voucher.nonce,
                    voucher.deadline
                )
            )
        );
        if (ECDSA.recover(digest, signature) != effectiveSigner(poolId)) revert InvalidVoucher();

        bool zeroForOne = Currency.unwrap(key.currency1) == voucher.token;
        address output = Currency.unwrap(zeroForOne ? key.currency1 : key.currency0);
        address input = Currency.unwrap(zeroForOne ? key.currency0 : key.currency1);
        if (output != voucher.token || input != address(weth)) revert InvalidPool();

        usedNonces[voucher.buyer][voucher.nonce] = true;
        bytes32 authorizationKey = _authorizationKey(voucher.buyer, poolId, voucher.nonce);
        assembly ("memory-safe") {
            tstore(authorizationKey, 1)
        }

        uint256 tokenBefore = IERC20(voucher.token).balanceOf(address(this));
        uint256 wethBefore = weth.balanceOf(address(this));
        (bytes memory commands, bytes[] memory inputs) = UniversalRouterV211Encoder.exactOutputWethBuy(
            key,
            zeroForOne,
            voucher.tokenOut,
            voucher.maxWethIn,
            abi.encode(voucher.buyer, poolId, voucher.nonce),
            address(weth),
            address(this)
        );
        universalRouter.execute{ value: msg.value }(commands, inputs, voucher.deadline);

        assembly ("memory-safe") {
            tstore(authorizationKey, 0)
        }
        tokenOut = IERC20(voucher.token).balanceOf(address(this)) - tokenBefore;
        if (tokenOut != voucher.tokenOut) revert InvalidVoucher();
        IERC20(voucher.token).safeTransfer(voucher.buyer, tokenOut);

        uint256 wethRefund = weth.balanceOf(address(this)) - wethBefore;
        wethSpent = voucher.maxWethIn - wethRefund;
        if (wethRefund != 0) {
            weth.withdraw(wethRefund);
            (bool ok,) = voucher.buyer.call{ value: wethRefund }("");
            if (!ok) revert TransferFailed();
        }
        emit BuyExecuted(voucher.buyer, poolId, voucher.nonce, wethSpent, tokenOut);
    }

    function isAuthorized(address buyer, PoolId poolId, uint256 nonce) external view returns (bool authorized) {
        bytes32 key = _authorizationKey(buyer, poolId, nonce);
        assembly ("memory-safe") {
            authorized := tload(key)
        }
    }

    function hashVoucher(BuyVoucher calldata voucher) external view returns (bytes32) {
        return _hashTypedDataV4(
            keccak256(
                abi.encode(
                    BUY_TYPEHASH,
                    voucher.buyer,
                    PoolId.unwrap(voucher.poolId),
                    voucher.token,
                    voucher.tokenOut,
                    voucher.maxWethIn,
                    voucher.nonce,
                    voucher.deadline
                )
            )
        );
    }

    function _authorizationKey(address buyer, PoolId poolId, uint256 nonce) private pure returns (bytes32) {
        return keccak256(abi.encodePacked("QUIVER_V4_BUY", buyer, PoolId.unwrap(poolId), nonce));
    }
}
Chain explorer1428msChain node89ms