QuiverToken
0xc7a8af2e5e5e2cf6c5257218cc93ad2debe17579
Verification
Verified
v0.8.26+commit.8a97fa7a
Type
Contract
6,158 bytes
ABI entries
34
16 read · 6 write
License
none
Contract information
- Address
- 0xc7a8af2e5e5e2cf6c5257218cc93ad2debe17579
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.26+commit.8a97fa7a
- Optimization
- Enabled
- Creator
- 0xf67eAb67fa…3debe9090f
- Creation tx
- 0xd337555f61…989b9d5de0
Token
- Name
- Quiver guild
- Symbol
- QUIVER
- Decimals
- 18
- Holders
- 930
- Market
- View token page →
Read contract (16)
allowance(address, address) → uint256
balanceOf(address) → uint256
contractURI() → string
creator() → address
decimals() → uint8
exclusive() → bool
factory() → address
gate() → address
gated() → bool
guardExempt(address) → bool
imageURI() → string
launcher() → address
name() → string
pool() → address
symbol() → string
totalSupply() → uint256
Events (4)
ApprovalExclusivityDisabledGatingEndedTransfer
ABI
[
{
"inputs": [
{
"internalType": "string",
"name": "name_",
"type": "string"
},
{
"internalType": "string",
"name": "symbol_",
"type": "string"
},
{
"internalType": "uint256",
"name": "totalSupply_",
"type": "uint256"
},
{
"internalType": "string",
"name": "imageURI_",
"type": "string"
},
{
"components": [
{
"internalType": "bool",
"name": "gated",
"type": "bool"
},
{
"internalType": "bool",
"name": "exclusive",
"type": "bool"
},
{
"internalType": "address",
"name": "gate",
"type": "address"
},
{
"internalType": "address",
"name": "creator",
"type": "address"
},
{
"internalType": "address",
"name": "factory",
"type": "address"
},
{
"internalType": "address[]",
"name": "exempt",
"type": "address[]"
}
],
"internalType": "struct QuiverToken.GuardConfig",
"name": "guard_",
"type": "tuple"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "BuyNotApproved",
"type": "error"
},
{
"inputs": [],
"name": "GuardConfigInvalid",
"type": "error"
},
{
"inputs": [],
"name": "OnlyCreator",
"type": "error"
},
{
"inputs": [],
"name": "OnlyLauncher",
"type": "error"
},
{
"inputs": [],
"name": "PoolAlreadySet",
"type": "error"
},
{
"inputs": [],
"name": "RivalPoolBlocked",
"type": "error"
},
{
"inputs": [],
"name": "TransferToZeroAddress",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "Approval",
"type": "event"
},
{
"anonymous": false,
"inputs": [],
"name": "ExclusivityDisabled",
"type": "event"
},
{
"anonymous": false,
"inputs": [],
"name": "GatingEnded",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "from",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "to",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "Transfer",
"type": "event"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "allowance",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "approve",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "balanceOf",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "contractURI",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "creator",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "decimals",
"outputs": [
{
"internalType": "uint8",
"name": "",
"type": "uint8"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "disableExclusivity",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "endGating",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "exclusive",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "factory",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "gate",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "gated",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "guardExempt",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "imageURI",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "launcher",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "name",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "pool",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "pool_",
"type": "address"
}
],
"name": "setPool",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "symbol",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "totalSupply",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "transfer",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "from",
"type": "address"
},
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "transferFrom",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
}
]Source code
// SPDX-License-Identifier: BUSL-1.1
// Copyright (c) 2026 Quiver (quiver-app.io)
// Licensed under the Business Source License 1.1, see LICENSE.
// Production use and forks are not permitted without a written grant
// from Quiver.
pragma solidity 0.8.26;
interface IQuiverGateMinimal {
function isApproved(address buyer) external view returns (bool);
}
interface IUniswapV3FactoryGetPool {
function getPool(
address tokenA,
address tokenB,
uint24 fee
) external view returns (address);
}
/// @title QuiverToken
/// @notice Fixed-supply ERC-20, deployed once per Quiver launch. No owner,
/// no minting, no burning. The whole supply goes to the deployer (the
/// QuiverLauncher) in the constructor.
///
/// Metadata lives on-chain: name/symbol/imageURI are set in the constructor
/// and contractURI() exposes them as ERC-7572 contract-level metadata (a
/// data-URI JSON), so explorers and tooling can pick up the token image
/// without any off-chain registry.
///
/// Launch guard - two OPT-IN, ONE-WAY toggles, both fixed at launch and only
/// ever switchable OFF (never back on) by the creator:
///
/// - gated: buys (transfers out of the main launch pool) require the
/// recipient to be approved by the QuiverGate for the current
/// transaction (an API-signed per-wallet voucher). Sells and
/// wallet-to-wallet transfers are NEVER blocked - gating can
/// never trap a holder. endGating() opens public trading
/// forever (the "end bonding" moment).
/// - exclusive: transfers to any OTHER Uniswap v3 pool of this token
/// revert (a pool that cannot receive tokens can never hold
/// liquidity), so the main launch pool captures all volume
/// and its fee tier can't be undercut by a rival pool. This
/// is POOL exclusivity, not position exclusivity: anyone can
/// still LP inside the main pool and share its fees (v3
/// pools are permissionless). disableExclusivity() lifts
/// this forever.
///
/// A public launch deploys with both flags false and behaves exactly like a
/// plain ERC-20: the guard short-circuits on a single storage read. All
/// guard config lives in STORAGE (not immutables) so every launch - public
/// or guarded - shares one canonical runtime bytecode, which is what the
/// Quiver API exact-matches before listing a pool.
contract QuiverToken {
string public name;
string public symbol;
uint8 public constant decimals = 18;
uint256 public immutable totalSupply;
/// @notice Token image URI (any scheme: https, ipfs, ...). Fixed at
/// deployment; consumers decide which sources they trust.
string public imageURI;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
// ------------------------------------------------------------------
// Launch guard state. `pool`/`gated`/`exclusive` pack into ONE slot,
// so the unguarded hot path costs a single extra sload.
// ------------------------------------------------------------------
/// @notice The main launch pool (token/WETH at the launch's fee tier),
/// set once by the launcher right after pool creation. address(0) on
/// public launches (the guard never needs it there).
address public pool;
/// @notice Buys-only gate toggle. One-way: endGating() only.
bool public gated;
/// @notice Rival-v3-pool block toggle. One-way: disableExclusivity() only.
bool public exclusive;
/// @notice The deployer (QuiverLauncher); only authority is setPool once.
address public launcher;
/// @notice The launch creator; can only ever LOOSEN the guard.
address public creator;
/// @notice The QuiverGate consulted for buy approval while gated.
address public gate;
/// @notice Canonical Uniswap v3 factory, used to identify rival pools.
address public factory;
/// @notice Addresses exempt from the buy gate as recipients. Set at
/// launch to infrastructure that legitimately receives tokens FROM the
/// pool while gated: the PermaLock (fee collection routes token-side
/// fees pool -> PermaLock), the QuoterV2 (buy quotes simulate a
/// pool -> quoter transfer via eth_call; a real transfer there only
/// strands the tokens, so this is not a bypass), and - when the launch
/// carried an atomic first buy - the QuiverLauncher (the first-buy swap
/// lands there before being forwarded to the creator; any later
/// transfer there only strands tokens too).
mapping(address => bool) public guardExempt;
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);
/// @notice The one-way "end bonding" moment: public trading is open forever.
event GatingEnded();
/// @notice One-way: other v3 pools of this token may exist from now on.
event ExclusivityDisabled();
error OnlyLauncher();
error OnlyCreator();
error PoolAlreadySet();
error GuardConfigInvalid();
error BuyNotApproved();
error RivalPoolBlocked();
error TransferToZeroAddress();
/// @notice Guard configuration, fixed at deployment (an all-zero config
/// is a public launch). Grouped into a struct to keep the constructor's
/// stack shallow.
struct GuardConfig {
bool gated;
bool exclusive;
/// The QuiverGate; required iff gated.
address gate;
/// The launch creator (endGating/disableExclusivity authority);
/// required iff any toggle is on.
address creator;
/// The canonical Uniswap v3 factory; required iff exclusive.
address factory;
/// Recipients exempt from the buy gate (launch infrastructure).
address[] exempt;
}
constructor(
string memory name_,
string memory symbol_,
uint256 totalSupply_,
string memory imageURI_,
GuardConfig memory guard_
) {
name = name_;
symbol = symbol_;
totalSupply = totalSupply_;
imageURI = imageURI_;
// Guard wiring, written only when a toggle is on (public launches
// pay no storage for config they don't use).
if (guard_.gated && guard_.gate == address(0))
revert GuardConfigInvalid();
if (guard_.exclusive && guard_.factory == address(0))
revert GuardConfigInvalid();
if ((guard_.gated || guard_.exclusive) && guard_.creator == address(0))
revert GuardConfigInvalid();
if (guard_.gated || guard_.exclusive) {
launcher = msg.sender;
creator = guard_.creator;
gated = guard_.gated;
exclusive = guard_.exclusive;
if (guard_.gated) {
gate = guard_.gate;
for (uint256 i = 0; i < guard_.exempt.length; i++) {
guardExempt[guard_.exempt[i]] = true;
}
}
if (guard_.exclusive) factory = guard_.factory;
}
balanceOf[msg.sender] = totalSupply_;
emit Transfer(address(0), msg.sender, totalSupply_);
}
// ========================================================================
// Launch guard
// ========================================================================
/// @notice Launcher-only, once: records the main launch pool so the
/// guard can tell buys/sells apart. Called between pool creation and
/// the LP mint inside the atomic launch transaction.
function setPool(address pool_) external {
if (msg.sender != launcher) revert OnlyLauncher();
if (pool != address(0)) revert PoolAlreadySet();
if (pool_ == address(0)) revert GuardConfigInvalid();
pool = pool_;
}
/// @notice Creator-only, one-way: opens public trading forever. There
/// is no way to re-enable gating.
function endGating() external {
if (msg.sender != creator) revert OnlyCreator();
if (!gated) revert GuardConfigInvalid();
gated = false;
emit GatingEnded();
}
/// @notice Creator-only, one-way: allows other v3 pools of this token
/// forever. There is no way to re-enable exclusivity.
function disableExclusivity() external {
if (msg.sender != creator) revert OnlyCreator();
if (!exclusive) revert GuardConfigInvalid();
exclusive = false;
emit ExclusivityDisabled();
}
/// @dev The transfer guard. Rules, in order:
/// 1. both toggles off (every public launch, and every guarded launch
/// after the creator ends both) -> plain ERC-20, one sload;
/// 2. transfers TO the main pool (sells, the launch LP mint) always
/// pass - no configuration can ever block an exit;
/// 3. while gated, transfers FROM the pool (buys) require the
/// recipient to be gate-approved for this transaction, unless the
/// recipient is launch-exempt infrastructure (PermaLock, quoter);
/// 4. while exclusive, transfers to a contract that proves to be a
/// canonical v3 pool of this token (self-reported token0/token1/
/// fee confirmed via factory.getPool == recipient) revert.
function _checkGuard(address from, address to) private view {
// pool + gated + exclusive share one slot: one sload for all three.
address pool_ = pool;
bool gated_ = gated;
bool exclusive_ = exclusive;
if (!gated_ && !exclusive_) return;
// UNCONDITIONAL EXIT GUARANTEE: a Uniswap v3 sell transfers tokens
// from the holder/router TO the pool. Return before every gate,
// exemption, exclusivity, or external-contract check, so neither the
// creator nor any configured dependency can ever block that sell.
if (to == pool_) return;
// The gate is deliberately the opposite direction only: pool -> buyer.
// It can reject a buy, but is unreachable for sells and wallet transfers.
if (gated_ && from == pool_ && !guardExempt[to]) {
if (!IQuiverGateMinimal(gate).isApproved(to))
revert BuyNotApproved();
}
if (exclusive_ && to.code.length > 0 && _isRivalV3Pool(to))
revert RivalPoolBlocked();
}
/// @dev True when `to` is a canonical Uniswap v3 pool with this token
/// on one side. The recipient's self-reported token0/token1/fee are
/// only trusted after factory.getPool(token0, token1, fee) confirms the
/// recipient IS that pool: a real pool reports its true immutables (so
/// no false negatives), and a liar gets a different address back from
/// the factory (so no false positives).
function _isRivalV3Pool(address to) private view returns (bool) {
(bool ok0, bytes memory data0) = to.staticcall(
abi.encodeWithSignature("token0()")
);
if (!ok0 || data0.length != 32) return false;
(bool ok1, bytes memory data1) = to.staticcall(
abi.encodeWithSignature("token1()")
);
if (!ok1 || data1.length != 32) return false;
address token0 = _asAddress(data0);
address token1 = _asAddress(data1);
if (token0 != address(this) && token1 != address(this)) return false;
(bool okFee, bytes memory dataFee) = to.staticcall(
abi.encodeWithSignature("fee()")
);
if (!okFee || dataFee.length != 32) return false;
uint24 fee = uint24(uint256(bytes32(dataFee)));
return
IUniswapV3FactoryGetPool(factory).getPool(token0, token1, fee) ==
to;
}
/// @dev Lower-160-bits decode that can't revert on dirty return data.
function _asAddress(bytes memory data) private pure returns (address) {
return address(uint160(uint256(bytes32(data))));
}
// ========================================================================
// ERC-20
// ========================================================================
/// @notice ERC-7572 contract-level metadata as a self-contained data URI,
/// so there's no off-chain JSON to host. name/symbol/imageURI are user
/// input and get JSON-escaped before embedding.
function contractURI() external view returns (string memory) {
return
string(
abi.encodePacked(
'data:application/json;utf8,{"name":"',
_jsonEscape(name),
'","symbol":"',
_jsonEscape(symbol),
'","image":"',
_jsonEscape(imageURI),
'"}'
)
);
}
function transfer(address to, uint256 value) external returns (bool) {
_transfer(msg.sender, to, value);
return true;
}
function approve(address spender, uint256 value) external returns (bool) {
allowance[msg.sender][spender] = value;
emit Approval(msg.sender, spender, value);
return true;
}
function transferFrom(
address from,
address to,
uint256 value
) external returns (bool) {
uint256 allowed = allowance[from][msg.sender];
if (allowed != type(uint256).max) {
allowance[from][msg.sender] = allowed - value;
}
_transfer(from, to, value);
return true;
}
function _transfer(address from, address to, uint256 value) internal {
// Reject the zero address (OpenZeppelin ERC-20 behavior): the supply
// is fixed and there is no burn, so tokens must never be strandable
// at address(0) either.
if (to == address(0)) revert TransferToZeroAddress();
_checkGuard(from, to);
balanceOf[from] -= value;
balanceOf[to] += value;
emit Transfer(from, to, value);
}
/// @dev Escapes `"` and `\` and strips control characters. That's enough
/// to keep the contractURI JSON well-formed for arbitrary user strings.
function _jsonEscape(
string memory value
) private pure returns (string memory) {
bytes memory input = bytes(value);
bytes memory out = new bytes(input.length * 2);
uint256 j;
for (uint256 i = 0; i < input.length; i++) {
bytes1 c = input[i];
if (c == '"' || c == "\\") {
out[j++] = "\\";
out[j++] = c;
} else if (uint8(c) >= 0x20) {
out[j++] = c;
}
}
assembly {
mstore(out, j)
}
return string(out);
}
}
Chain explorer3697msChain node75ms