StockTreasury

0x1604ff11dfeaac437077aeda2fa492ac9ec804df

Verification
Verified
v0.8.26+commit.8a97fa7a
Type
Contract
3,589 bytes
ABI entries
29
7 read · 8 write
License
none

Contract information

Address
0x1604ff11dfeaac437077aeda2fa492ac9ec804df
Chain
Robinhood Chain (4663)
Compiler
v0.8.26+commit.8a97fa7a
Optimization
Enabled
Creation tx
0xdaf0834ea1…cab54c8d7b

Token

Not a token

This contract does not expose ERC-20 metadata.

Read contract (7)

distributor()address
keeper(address)bool
owner()address
poolManager()address
stockTokenAt(uint256)address
stocks(uint256)address, uint24, int24, address
stocksLength()uint256

Events (4)

OwnershipTransferredStockAddedStockRemovedStocksBought

ABI

[
  {
    "inputs": [
      {
        "internalType": "contract IPoolManager",
        "name": "_pm",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "owner_",
        "type": "address"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "inputs": [],
    "name": "LengthMismatch",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "NoStocks",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "NothingToSpend",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "OnlyKeeper",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "OnlyPoolManager",
    "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": [
      {
        "internalType": "uint256",
        "name": "i",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "out",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "minOut",
        "type": "uint256"
      }
    ],
    "name": "Slippage",
    "type": "error"
  },
  {
    "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": "address",
        "name": "token",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint24",
        "name": "fee",
        "type": "uint24"
      },
      {
        "indexed": false,
        "internalType": "int24",
        "name": "tickSpacing",
        "type": "int24"
      },
      {
        "indexed": false,
        "internalType": "address",
        "name": "hooks",
        "type": "address"
      }
    ],
    "name": "StockAdded",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "token",
        "type": "address"
      }
    ],
    "name": "StockRemoved",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "ethSpent",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "stockCount",
        "type": "uint256"
      }
    ],
    "name": "StocksBought",
    "type": "event"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "internalType": "uint24",
        "name": "fee",
        "type": "uint24"
      },
      {
        "internalType": "int24",
        "name": "tickSpacing",
        "type": "int24"
      },
      {
        "internalType": "address",
        "name": "hooks",
        "type": "address"
      }
    ],
    "name": "addStock",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256[]",
        "name": "minOuts",
        "type": "uint256[]"
      }
    ],
    "name": "buyStocks",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "distributor",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "name": "keeper",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "owner",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "poolManager",
    "outputs": [
      {
        "internalType": "contract IPoolManager",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "i",
        "type": "uint256"
      }
    ],
    "name": "removeStock",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "renounceOwnership",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "d",
        "type": "address"
      }
    ],
    "name": "setDistributor",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "k",
        "type": "address"
      },
      {
        "internalType": "bool",
        "name": "on",
        "type": "bool"
      }
    ],
    "name": "setKeeper",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "i",
        "type": "uint256"
      }
    ],
    "name": "stockTokenAt",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "name": "stocks",
    "outputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "internalType": "uint24",
        "name": "fee",
        "type": "uint24"
      },
      {
        "internalType": "int24",
        "name": "tickSpacing",
        "type": "int24"
      },
      {
        "internalType": "address",
        "name": "hooks",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "stocksLength",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "newOwner",
        "type": "address"
      }
    ],
    "name": "transferOwnership",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes",
        "name": "data",
        "type": "bytes"
      }
    ],
    "name": "unlockCallback",
    "outputs": [
      {
        "internalType": "bytes",
        "name": "",
        "type": "bytes"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "stateMutability": "payable",
    "type": "receive"
  }
]

Source code

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {IPoolManager} from "v4-core/src/interfaces/IPoolManager.sol";
import {IUnlockCallback} from "v4-core/src/interfaces/callback/IUnlockCallback.sol";
import {IHooks} from "v4-core/src/interfaces/IHooks.sol";
import {PoolKey} from "v4-core/src/types/PoolKey.sol";
import {Currency} from "v4-core/src/types/Currency.sol";
import {SwapParams} from "v4-core/src/types/PoolOperation.sol";
import {BalanceDelta} from "v4-core/src/types/BalanceDelta.sol";
import {TickMath} from "v4-core/src/libraries/TickMath.sol";

/// Accumulates the native-ETH taxes IndexFeeHook sends here, and on keeper call splits
/// the pot equally across the registered tokenized stocks, buying each through its
/// Uniswap v4 native-ETH pool (e.g. TSLA/ETH). Bought stock goes straight to the
/// StockDistributor.
/// ponytail: ETH-quoted v4 pools only — the only live stock liquidity today; add a USDG leg when needed.
contract StockTreasury is Ownable, IUnlockCallback {
    IPoolManager public immutable poolManager;

    address public distributor;
    mapping(address => bool) public keeper; // trusted callers who supply minOuts

    struct Stock {
        address token;
        uint24 fee;
        int24 tickSpacing;
        address hooks;
    }

    Stock[] public stocks;

    event StockAdded(address indexed token, uint24 fee, int24 tickSpacing, address hooks);
    event StockRemoved(address indexed token);
    event StocksBought(uint256 ethSpent, uint256 stockCount);

    error OnlyKeeper();
    error OnlyPoolManager();
    error NoStocks();
    error NothingToSpend();
    error LengthMismatch();
    error Slippage(uint256 i, uint256 out, uint256 minOut);

    constructor(IPoolManager _pm, address owner_) Ownable(owner_) {
        poolManager = _pm;
    }

    function setDistributor(address d) external onlyOwner { distributor = d; }
    function setKeeper(address k, bool on) external onlyOwner { keeper[k] = on; }

    function addStock(address token, uint24 fee, int24 tickSpacing, address hooks) external onlyOwner {
        stocks.push(Stock(token, fee, tickSpacing, hooks));
        emit StockAdded(token, fee, tickSpacing, hooks);
    }

    function removeStock(uint256 i) external onlyOwner {
        emit StockRemoved(stocks[i].token);
        stocks[i] = stocks[stocks.length - 1];
        stocks.pop();
    }

    function stocksLength() external view returns (uint256) { return stocks.length; }
    function stockTokenAt(uint256 i) external view returns (address) { return stocks[i].token; }

    /// Buy each registered stock with an equal slice of the held ETH.
    /// Keeper-gated because the caller chooses minOuts (slippage protection).
    function buyStocks(uint256[] calldata minOuts) external {
        if (!keeper[msg.sender] && msg.sender != owner()) revert OnlyKeeper();
        uint256 n = stocks.length;
        if (n == 0) revert NoStocks();
        if (minOuts.length != n) revert LengthMismatch();

        uint256 per = address(this).balance / n;
        if (per == 0) revert NothingToSpend();

        poolManager.unlock(abi.encode(per, minOuts));
        emit StocksBought(per * n, n);
    }

    function unlockCallback(bytes calldata data) external returns (bytes memory) {
        if (msg.sender != address(poolManager)) revert OnlyPoolManager();
        (uint256 per, uint256[] memory minOuts) = abi.decode(data, (uint256, uint256[]));

        for (uint256 i; i < stocks.length; ++i) {
            Stock memory s = stocks[i];
            PoolKey memory key = PoolKey({
                currency0: Currency.wrap(address(0)), // native ETH always sorts first
                currency1: Currency.wrap(s.token),
                fee: s.fee,
                tickSpacing: s.tickSpacing,
                hooks: IHooks(s.hooks)
            });
            BalanceDelta delta = poolManager.swap(
                key,
                SwapParams({
                    zeroForOne: true,
                    amountSpecified: -int256(per), // exact input
                    sqrtPriceLimitX96: TickMath.MIN_SQRT_PRICE + 1
                }),
                ""
            );
            uint256 out = uint256(uint128(delta.amount1()));
            if (out < minOuts[i]) revert Slippage(i, out, minOuts[i]);
            poolManager.settle{value: per}();
            poolManager.take(Currency.wrap(s.token), distributor, out);
        }
        return "";
    }

    receive() external payable {} // taxes arrive as native ETH from the hook
}
Chain explorer4528msChain node80ms