// SPDX-License-Identifier: MIT pragma solidity 0.8.26; import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import {WeirVault, IUniswapV3Pool} from "./WeirVault.sol"; interface IUniswapV3Factory { function getPool(address, address, uint24) external view returns (address); } /// @title WeirFactory — opens a WeirVault for any USDG pool on one Uniswap v3 factory /// @notice Anyone may open a vault; each (pool, width) gets exactly one, at an address fixed in advance by /// CREATE2. The factory keeps no money and has no owner. contract WeirFactory { IUniswapV3Factory public immutable uniswap; address public immutable usdg; mapping(address pool => mapping(int24 halfWidth => address)) public vaultOf; address[] public allVaults; error NotAPool(); error NotUsdgPool(); error BadWidth(); error Exists(); event VaultCreated(address indexed pool, int24 halfWidth, address vault, string symbol); constructor(IUniswapV3Factory uniswap_, address usdg_) { uniswap = uniswap_; usdg = usdg_; } function vaultCount() external view returns (uint256) { return allVaults.length; } function create(IUniswapV3Pool pool, int24 halfWidth) external returns (address vault) { (bytes memory init, string memory symbol) = _init(pool, halfWidth); if (vaultOf[address(pool)][halfWidth] != address(0)) revert Exists(); bytes32 salt = keccak256(abi.encode(pool, halfWidth)); assembly { vault := create2(0, add(init, 32), mload(init), salt) } require(vault != address(0)); vaultOf[address(pool)][halfWidth] = vault; allVaults.push(vault); emit VaultCreated(address(pool), halfWidth, vault, symbol); } /// @notice Where `create(pool, halfWidth)` puts (or has put) the vault. function vaultAddress(IUniswapV3Pool pool, int24 halfWidth) external view returns (address) { (bytes memory init, ) = _init(pool, halfWidth); bytes32 salt = keccak256(abi.encode(pool, halfWidth)); return address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, keccak256(init)))))); } function _init(IUniswapV3Pool pool, int24 halfWidth) internal view returns (bytes memory init, string memory symbol) { address t0 = pool.token0(); address t1 = pool.token1(); uint24 fee = pool.fee(); if (uniswap.getPool(t0, t1, fee) != address(pool)) revert NotAPool(); address base; if (t0 == usdg) base = t1; else if (t1 == usdg) base = t0; else revert NotUsdgPool(); int24 s = pool.tickSpacing(); if (halfWidth < s || halfWidth % s != 0 || halfWidth > 443_600) revert BadWidth(); string memory sym = IERC20Metadata(base).symbol(); symbol = string.concat("weir", sym); init = abi.encodePacked( type(WeirVault).creationCode, abi.encode(pool, halfWidth, string.concat("Weir ", sym), symbol) ); } }